From e830d6f1243e0157cabab5ae284bb04d6085cc11 Mon Sep 17 00:00:00 2001 From: Paul Kimpel Date: Thu, 29 Dec 2016 10:27:45 -0800 Subject: [PATCH] Commit preliminary 0.00a emulator files for Processor and Control Console cloned from retro-205. --- emulator/B220Processor.js | 3422 ++++++++++++++++++++ index.html | 10 +- webUI/B220.css | 75 + webUI/B220.html | 85 + webUI/B220.js | 175 + webUI/B220Common.css | 553 ++++ webUI/B220ControlConsole.css | 330 ++ webUI/B220ControlConsole.html | 145 + webUI/B220ControlConsole.js | 603 ++++ webUI/B220Manifest.appcache | 61 + webUI/B220PanelUtil.js | 758 +++++ webUI/B220SetCallback.js | 275 ++ webUI/B220SystemConfig.css | 100 + webUI/B220SystemConfig.html | 464 +++ webUI/B220SystemConfig.js | 475 +++ webUI/B220Util.js | 171 + webUI/resources/B220-Logo - White.jpg | Bin 0 -> 3188 bytes webUI/resources/B220-Logo.jpg | Bin 0 -> 2982 bytes webUI/resources/B220-Site.jpg | Bin 0 -> 89629 bytes webUI/resources/Burroughs-Logo - White.jpg | Bin 0 -> 40126 bytes webUI/resources/Burroughs-Logo-Neg.jpg | Bin 0 -> 40107 bytes webUI/resources/Burroughs-Logo.jpg | Bin 0 -> 33691 bytes webUI/resources/Burroughs-Meatball.png | Bin 0 -> 1411 bytes webUI/resources/DataFileTapeHead.png | Bin 0 -> 163 bytes webUI/resources/MagTapeReel.jpg | Bin 0 -> 11900 bytes webUI/resources/ToggleDown.png | Bin 0 -> 8801 bytes webUI/resources/ToggleMid.png | Bin 0 -> 8527 bytes webUI/resources/ToggleUp.png | Bin 0 -> 8480 bytes webUI/resources/retro-Logo.png | Bin 0 -> 410 bytes 29 files changed, 7697 insertions(+), 5 deletions(-) create mode 100644 emulator/B220Processor.js create mode 100644 webUI/B220.css create mode 100644 webUI/B220.html create mode 100644 webUI/B220.js create mode 100644 webUI/B220Common.css create mode 100644 webUI/B220ControlConsole.css create mode 100644 webUI/B220ControlConsole.html create mode 100644 webUI/B220ControlConsole.js create mode 100644 webUI/B220Manifest.appcache create mode 100644 webUI/B220PanelUtil.js create mode 100644 webUI/B220SetCallback.js create mode 100644 webUI/B220SystemConfig.css create mode 100644 webUI/B220SystemConfig.html create mode 100644 webUI/B220SystemConfig.js create mode 100644 webUI/B220Util.js create mode 100644 webUI/resources/B220-Logo - White.jpg create mode 100644 webUI/resources/B220-Logo.jpg create mode 100644 webUI/resources/B220-Site.jpg create mode 100644 webUI/resources/Burroughs-Logo - White.jpg create mode 100644 webUI/resources/Burroughs-Logo-Neg.jpg create mode 100644 webUI/resources/Burroughs-Logo.jpg create mode 100644 webUI/resources/Burroughs-Meatball.png create mode 100644 webUI/resources/DataFileTapeHead.png create mode 100644 webUI/resources/MagTapeReel.jpg create mode 100644 webUI/resources/ToggleDown.png create mode 100644 webUI/resources/ToggleMid.png create mode 100644 webUI/resources/ToggleUp.png create mode 100644 webUI/resources/retro-Logo.png diff --git a/emulator/B220Processor.js b/emulator/B220Processor.js new file mode 100644 index 0000000..4c3683b --- /dev/null +++ b/emulator/B220Processor.js @@ -0,0 +1,3422 @@ +/*********************************************************************** +* retro-220/emulator B220Processor.js +************************************************************************ +* Copyright (c) 2017, Paul Kimpel. +* Licensed under the MIT License, see +* http://www.opensource.org/licenses/mit-license.php +************************************************************************ +* Burroughs 220 Emulator Processor (CPU) module. +* +* Instance variables in all caps generally refer to register or flip-flop (FF) +* entities in the processor hardware. See the following documents: +* +* Burroughs 205 Handbook +* (Bulletin 3021, Burroughs Corporation, 1956). +* Programming and Coding Manual, Datatron +* (Bulletin 3040A, ElectroData Corporation, 1954). +* Handbook of Operating Procedures for the Burroughs 205 +* (Bulletin 3034A, Burroughs Corporation, May 1960). +* +* available at: +* http://bitsavers.org/pdf/burroughs/electrodata/205/ +* +* also: +* +* TM4001 Datatron 205 Computer (Training Edition) +* (Burroughs Corporation, December,1956). +* Burroughs 205 Handbook: Floating Point Control Unit Model 36044 +* (Bulletin 3028, Burroughs Corporation 1957). +* Engineering Description of the ElectroData Digital Computer, J. C. Alrich, +* (IRE Transactions on Electronic Computers, vol EC-4, Number 1, March 1955). +* +* Datatron 205 word format: +* 44 bits, encoded as binary-coded decimal (BCD); non-decimal codes are invalid +* and cause the computer to stop with a Forbidden Combination (FC) alarm. +* High-order 4 bits are the "sign digit": +* Low-order bit of this digit is the actual sign. +* Higher-order bits are used in some I/O operations. +* Remaining 40 bits are the value as: +* 10 decimal digits as a fractional mantissa, with the decimal point between +* the sign and high-order (10th) digits +* 5 character codes +* one instruction word +* +* Instruction word format: +* Low-order 4 digits: operand address +* Next-higher 2 digits: operation code +* Next-higher 4 digits: breakpoint and special-function codes +* Sign digit: odd value indicates the B register is to be added to the +* operand address prior to execution. +* +* Processor timing is maintained internally in units of "word-times": 1/200-th +* revolution of the memory drum, or about 84 µs at 3570rpm. +* +************************************************************************ +* 2017-01-01 P.Kimpel +* Original version, cloned from a bit of retro-205 code. +***********************************************************************/ +"use strict"; + +/**************************************/ +function B220Processor(config, devices) { + /* Constructor for the 205 Processor module object */ + + // Emulator control + this.cardatron = null; // Reference to Cardatron Control Unit + this.config = config; // Reference to SystemConfig object + this.console = null; // Reference to Control Console for I/O + this.devices = devices; // Hash of I/O device objects + this.ioCallback = null; // Current I/O interface callback function + this.magTape = null; // Reference to Magnetic Tape Control Unit + this.poweredOn = 0; // System is powered on and initialized + this.successor = null; // Current delayed-action successor function + + // Memory + this.memoryDrum = new ArrayBuffer(4080*8); // Drum: 4080 64-bit FP words + this.MM = new Float64Array(this.memoryDrum, 0, 4000); // Main memory, 4000 words + this.L4 = new Float64Array(this.memoryDrum, 4000*8, 20); // 4000 loop, 20 words + this.L5 = new Float64Array(this.memoryDrum, 4020*8, 20); // 5000 loop, 20 words + this.L6 = new Float64Array(this.memoryDrum, 4040*8, 20); // 6000 loop, 20 words + this.L7 = new Float64Array(this.memoryDrum, 4060*8, 20); // 7000 loop, 20 words + + // Supervisory Panel switches + this.sswLockNormal = 0; // Lock/Normal switch + this.sswStepContinuous = 0; // Step/Continuous switch + this.sswAudibleAlarm = 0; // Audible alarm + + // Control Console switches + this.cswPOSuppress = 0; // Print-out suppress + this.cswSkip = 0; // Skip instruction + this.cswAudibleAlarm = 0; // Audible alarm + this.cswOutput = 0; // Output knob: 0=Off, 1=Page, 2=Tape (mapped from actual knob values) + this.cswInput = 0; // Input knob: 0=Mechanical reader, 1=Optical reader, 2=Keyboard + this.cswBreakpoint = 0; // Breakpoint knob: 0=Off, 1, 2, 4 + + // Mag-Tape Control Unit switch + this.tswSuppressB = 0; // Suppress B-register modification on input + + // Context-bound routines + this.boundExecuteComplete = B220Processor.bindMethod(this, B220Processor.prototype.executeComplete); + 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.boundConsoleOutputFinished = B220Processor.bindMethod(this, B220Processor.prototype.consoleOutputFinished); + this.boundConsoleInputDigit = B220Processor.bindMethod(this, B220Processor.prototype.consoleInputDigit); + 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); + this.boundMagTapeInitiateSend = B220Processor.bindMethod(this, B220Processor.prototype.magTapeInitiateSend); + this.boundMagTapeSendBlock = B220Processor.bindMethod(this, B220Processor.prototype.magTapeSendBlock); + this.boundMagTapeTerminateSend = B220Processor.bindMethod(this, B220Processor.prototype.magTapeTerminateSend); + + // Processor throttling control + this.scheduler = 0; // Current setCallback token + this.procStart = 0; // Javascript time that the processor started running, ms + this.procTime = 0; // Total processor running time, ms + + // External switches [used by EXC (71)] + this.externalSwitch = [0, 0, 0, 0, 0, 0, 0, 0]; + + // Register average-intensity accumulators + this.glowTimer = null; + this.toggleGlow = { + glowA: new Float64Array(44), + glowB: new Float64Array(16), + glowC: new Float64Array(40), + glowD: new Float64Array(44), + glowR: new Float64Array(40), + glowCtl: new Float64Array(40), + glowADDER: new Float64Array(4), + glowCT: new Float64Array(5), + glowTiming: 0, + glowOverflow: 0, + glowTWA: 0, + glow3IO: 0, + glowMAIN: 0, + glowRWM: 0, + glowRWL: 0, + glowWDBL: 0, + glowACTION: 0, + glowACCESS: 0, + glowLM: 0, + glowL4: 0, + glowL5: 0, + glowL6: 0, + glowL7: 0}; + + this.clear(); // Create and initialize the processor state + + this.loadDefaultProgram(); // Preload a default program +} + +/**************************************/ + +/* Global constants */ +B220Processor.version = "0.00a"; + +B220Processor.drumRPM = 3570; // memory drum speed, RPM +B220Processor.trackSize = 200; // words per drum revolution +B220Processor.loopSize = 20; // words per high-speed loop +B220Processor.wordTime = 60000/B220Processor.drumRPM/B220Processor.trackSize; + // one word time, about 0.084 ms at 3570rpm (=> 142.8 KHz) +B220Processor.wordsPerMilli = 1/B220Processor.wordTime; + // word times per millisecond +B220Processor.neonPersistence = 1000/30; + // persistence of neon bulb glow [ms] +B220Processor.maxGlowTime = B220Processor.neonPersistence*B220Processor.wordsPerMilli; + // panel bulb glow persistence [word-times] +B220Processor.lampGlowInterval = 50; // background lamp sampling interval (ms) +B220Processor.adderGlowAlpha = B220Processor.wordTime/12/B220Processor.neonPersistence; + // adder and carry toggle glow decay factor, + // based on one digit (1/12 word) time + +B220Processor.pow2 = [ // powers of 2 from 0 to 52 + 0x1, 0x2, 0x4, 0x8, + 0x10, 0x20, 0x40, 0x80, + 0x100, 0x200, 0x400, 0x800, + 0x1000, 0x2000, 0x4000, 0x8000, + 0x10000, 0x20000, 0x40000, 0x80000, + 0x100000, 0x200000, 0x400000, 0x800000, + 0x1000000, 0x2000000, 0x4000000, 0x8000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x100000000, 0x200000000, 0x400000000, 0x800000000, + 0x1000000000, 0x2000000000, 0x4000000000, 0x8000000000, + 0x10000000000, 0x20000000000, 0x40000000000, 0x80000000000, + 0x100000000000, 0x200000000000, 0x400000000000, 0x800000000000, + 0x1000000000000, 0x2000000000000, 0x4000000000000, 0x8000000000000, + 0x10000000000000]; + +B220Processor.mask2 = [ // (2**n)-1 for n from 0 to 52 + 0x0, 0x1, 0x3, 0x7, + 0x0F, 0x1F, 0x3F, 0x7F, + 0x0FF, 0x1FF, 0x3FF, 0x7FF, + 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, + 0x0FFFF, 0x1FFFF, 0x3FFFF, 0x7FFFF, + 0x0FFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, + 0x0FFFFFF, 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, + 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, + 0x0FFFFFFFF, 0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, + 0x0FFFFFFFFF, 0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, + 0x0FFFFFFFFFF, 0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, + 0x0FFFFFFFFFFF, 0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF , 0x7FFFFFFFFFFF, + 0x0FFFFFFFFFFFF, 0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, + 0x0FFFFFFFFFFFFF] ; + +/**************************************/ +B220Processor.prototype.clear = function clear() { + /* Initializes (and if necessary, creates) the processor state */ + + this.clearControl(); + + // Registers + this.A = 0; // A register - accumulator + this.B = 0; // B register - index, loop control + this.C = 0; // C register - current operator, control counter + this.D = 0; // D register - input buffer from memory and I/O + this.R = 0; // R register - accumulator extension + + this.COP = 0; // copy of C register op code (2 digits) + this.CADDR = 0; // copy of C register operand address (4 digits) + this.CCONTROL = 0; // copy of C register control address (4 digits) + this.CEXTRA = 0; // high-order four digits of instruction word + sign + + // Adder registers + this.ADDER = 0; // The adder + this.CT = 0; // Carry toggles for the adder + + // Operational toggles + this.togTiming = 1; // Timing toggle: 0=execute, 1=fetch + this.togCST = 1; // Computer Stop toggle (?) + this.cctContinuous = 0; // Console step(0) / continuous(1) toggle + + // Halt/error toggles + this.stopOverflow = 0; // Halted due to overflow + this.stopSector = 0; // Halted due to sector alarm + this.stopForbidden = 0; // Halted due to forbidden combination + this.stopControl = 0; // Halted due to Stop operator (08) or overflow + this.stopBreakpoint = 0; // Halted due to breakpoint + this.stopIdle = 1; // Halted or in step mode + + // Memory control toggles + this.memMAIN = 0; // Access is to main memory + this.memRWM = 0; // Read/write main memory + this.memRWL = 0; // Read/write loop memory + this.memWDBL = 0; // Word or block transfer + this.memACTION = 0; // Memory access ACTION toggle + this.memACCESS = 0; // Memory access active toggle + this.memLM = 0; // Loop/main access toggle + this.memL4 = 0; // 4000 loop access + this.memL5 = 0; // 5000 loop access + this.memL6 = 0; // 6000 loop access + this.memL7 = 0; // 7000 loop access + + // Statistics timers + this.lastGlowTime = 0; // Last panel lamp intensity update time [word-times] + this.memoryStartTime = 0; // Start of last memory access [word-times] + this.memoryStopTime = 0; // End of last memory access [word-times] + + this.setTimingToggle(0); // set to Execute initially + this.sampleLamps(1.0, 1.0); // initialize the lamp-glow averages + + // 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(); + } +}; + +/**************************************/ +B220Processor.prototype.clearControlToggles = function clearControlToggles() { + /* Clears the processor control toggles at end of an execution cycle. + The adder and carry toggles should also be cleared as part of this, but + they are often zero anyway, and we are only doing this for display purposes, + so leave them as is */ + + this.togADDER = // Adder toggle + this.togBTOAIN = 0; // B-to-A, Input toggle + this.togDPCTR = // Digit-pulse toggle: on during decimal-correct, off during complement + this.togDELTABDIV = // Delta-B, divide toggle + this.togCOMPL = // Complement toggle + this.togADDAB = // Add-A/add-B toggle + this.togCLEAR = // Clear toggle + this.togMULDIV = // Multiply-divide toggle + this.togSIGN = // Sign toggle + this.togCOUNT = // Count toggle + this.togDIVALARM = // Divide alarm + this.togSTEP = 0; // Step toggle +}; + +/**************************************/ +B220Processor.prototype.clearControl = function clearControl() { + /* Initializes (and if necessary, creates) the processor control registers + and toggles */ + + this.SHIFT = 0; // Shift counter + this.SHIFTCONTROL = 0; // Shift control register + this.SPECIAL = 0; // Special counter + + // Toggles (flip-flops) + this.clearControlToggles(); // the standard set + this.togSTART = 0; // Input control: Start toggle + this.togTF = 0; // Input control: FINISH pulse toggle + this.togTC1 = 0; // Input control: clock pulse toggle (from input device) + this.togTC2 = 0; // Input control: clock pulse toggle (shift input digit to D sign) + this.togOK = 0; // Output control: OK toggle (output device ready for next digit) + this.togPO1 = 0; // Output control: PO1 toggle (actual print-out to begin) + this.togPO2 = 0; // Output control: PO2 toggle (on at start of digit output) + this.togDELAY = 0; // Output control: Delay toggle + this.togT0 = 0; // Central control: T0 toggle + this.togBKPT = 0; // Central control: breakpoint toggle + this.togZCT = 0; // Central control: zero check toggle + this.togASYNC = 0; // Central control: async toggle + this.togMT3P = 0; // Magnetic tape: 3P toggle + this.togMT1BV4 = 0; // Magnetic tape: 1BV4 toggle + this.togMT1BV5 = 0; // Magnetic tape: 1BV5 toggle + + // Cardatron toggles + this.togTWA = 0; // Cardatron: TWA toggle + this.tog3IO = 0; // Cardatron: 3IO toggle + + // I/O globals + this.kDigit = 0; // variant/format digits from upper part of instruction + this.selectedUnit = 0; // currently-selected unit number +}; + +/*********************************************************************** +* Utility Functions * +***********************************************************************/ + +/**************************************/ +B220Processor.bindMethod = function bindMethod(context, f) { + /* Returns a new function that binds the function "f" to the object "context". + Note that this is a static constructor property function, NOT an instance + method of the CC object */ + + return function bindMethodAnon() {return f.apply(context, arguments)}; +}; + +/**************************************/ +B220Processor.bcdBinary = function bcdBinary(v) { + /* Converts the BCD value "v" to a binary number and returns it */ + var d; + var power = 1; + var result = 0; + + while(v) { + d = v % 0x10; + result += d*power; + power *= 10; + v = (v-d)/0x10; + } + return result; +}; + +/**************************************/ +B220Processor.binaryBCD = function binaryBCD(v) { + /* Converts the binary value "v" to a BCD number and returns it */ + var d; + var power = 1; + var result = 0; + + while(v) { + d = v % 10; + result += d*power; + power *= 0x10; + v = (v-d)/10; + } + return result; +}; + + +/*********************************************************************** +* Timing and Statistics Functions * +***********************************************************************/ + +/**************************************/ +B220Processor.prototype.setTimingToggle = function setTimingToggle(cycle) { + /* Sets the timing toggle to the value of "cycle": 0=Execute, !0=Fetch */ + + this.togTiming = (cycle ? 1 : 0); +}; + +/**************************************/ +B220Processor.prototype.setOverflow = function setOverflow(overflow) { + /* Sets the overflow toggle to the value of "overflow" */ + + this.stopOverflow = (overflow ? 1 : 0); +}; + +/**************************************/ +B220Processor.prototype.feelTheGlow = function feelTheGlow(alpha, alpha1, glow, bits, r) { + /* Computes the running exponential average of lamp intensities for register + "r" in the low-order "bits" bits, using the "alpha" decay factor into the array + "glow". alpha1 must be 1.0 - alpha */ + var b = 0; + var bit; + + while (r) { + bit = r % 2; + r = (r-bit)/2; + glow[b] = glow[b]*alpha1 + bit*alpha; + ++b; + } + + while (b < bits) { + glow[b] *= alpha1; + ++b; + } +}; + +/**************************************/ +B220Processor.prototype.updateAdderGlow = function updateAdderGlow(adder, ct) { + /* Computes the exponential running average of adder and carry toggle bit + intensities. This is called for every digit passing through the adder, and + serves only to compute a range of bit-by-bit intensities (0,1) for display */ + var alpha = B220Processor.adderGlowAlpha; + var alpha1 = 1.0 - alpha; + var b = 0; + var bit; + var glowA = this.toggleGlow.glowADDER; + var glowC = this.toggleGlow.glowCT; + + while (b < 4) { + bit = adder % 2; + adder = (adder-bit)/2; + glowA[b] = glowA[b]*alpha1 + bit*alpha; + ++b; + } + + b = 0; + while (b < 5) { + bit = ct % 2; + ct = (ct-bit)/2; + glowC[b] = glowC[b]*alpha1 + bit*alpha; + ++b; + } +}; + +/**************************************/ +B220Processor.prototype.sampleLamps = function sampleLamps(alpha, memAlpha) { + /* Updates the lamp intensity arrays for all registers. "alpha" and "memAlpha" + must be in the range (0-,1) and indicate the relative significance for the current + register settings to the running exponential average algorithm */ + var alpha1 = 1.0 - alpha; + var tg = this.toggleGlow; + + tg.glowTiming = tg.glowTiming*alpha1 + this.togTiming*alpha; + tg.glowOverflow = tg.glowOverflow*alpha1 + this.stopOverflow*alpha; + tg.glowTWA = tg.glowTWA*alpha1 + this.togTWA*alpha; + tg.glow3IO = tg.glow3IO*alpha1 + this.tog3IO*alpha; + + this.feelTheGlow(alpha, alpha1, tg.glowA, 44, this.A); + this.feelTheGlow(alpha, alpha1, tg.glowB, 16, this.B); + this.feelTheGlow(alpha, alpha1, tg.glowC, 40, this.C); + this.feelTheGlow(alpha, alpha1, tg.glowD, 44, this.D); + this.feelTheGlow(alpha, alpha1, tg.glowR, 40, this.R); + + this.feelTheGlow(alpha, alpha1, tg.glowADDER, 4, this.ADDER); + this.feelTheGlow(alpha, alpha1, tg.glowCT, 5, this.CT); + + this.feelTheGlow(alpha, alpha1, tg.glowCtl, 40, (((((((((((((((((((((((((((( + this.SPECIAL*2 + + this.togBTOAIN)*2 + + this.togADDER)*2 + + this.togDPCTR)*2 + + this.togDELTABDIV)*2 + + this.togCOMPL)*2 + + this.togADDAB)*2 + + this.togCLEAR)*2 + + this.togMULDIV)*2 + + this.togSIGN)*2 + + this.togCOUNT)*2 + + this.togDIVALARM)*2 + + this.togSTEP)*2 + + this.togSTART)*2 + + this.togTF)*2 + + this.togTC1)*2 + + this.togTC2)*2 + + this.togOK)*2 + + this.togPO1)*2 + + this.togPO2)*2 + + this.togDELAY)*2 + + this.togT0)*2 + + this.togBKPT)*2 + + this.togZCT)*2 + + this.togASYNC)*16 + + this.SHIFTCONTROL)*2 + + this.togMT3P)*2 + + this.togMT1BV4)*2 + + this.togMT1BV5)*32 + + this.SHIFT); + + // Decay the memory toggles if no memory access is in progress + if (this.memoryStartTime == 0) { + alpha1 = 1.0 - memAlpha; + tg.glowMAIN = tg.glowMAIN*alpha1; + tg.glowRWM = tg.glowRWM*alpha1; + tg.glowRWL = tg.glowRWL*alpha1; + tg.glowWDBL = tg.glowWDBL*alpha1; + tg.glowACTION = tg.glowACTION*alpha1; + tg.glowACCESS = tg.glowACCESS*alpha1; + tg.glowLM = tg.glowLM*alpha1; + tg.glowL4 = tg.glowL4*alpha1; + tg.glowL5 = tg.glowL5*alpha1; + tg.glowL6 = tg.glowL6*alpha1; + tg.glowL7 = tg.glowL7*alpha1; + + if (isNaN(tg.glowMAIN)) {debugger} + } +}; + +/**************************************/ +B220Processor.prototype.updateLampGlow = function updateLampGlow(drumTime) { + /* Computes an alpha factor based on the elapsed time since the last sampling, + then calls sampleLamps() to update the running averages. drumTime is the + current time in word-time units, 0.084ms. If drumTime is zero or undefined, + the current time is used */ + var clock = drumTime || performance.now()*B220Processor.wordsPerMilli; + var alpha = Math.min((clock - this.lastGlowTime)/B220Processor.maxGlowTime, 1.0); + var memAlpha = Math.min((clock - this.memoryStopTime)/B220Processor.maxGlowTime, 1.0); + + this.sampleLamps(alpha, memAlpha); + this.lastGlowTime = clock; +}; + +/**************************************/ +B220Processor.prototype.startMemoryTiming = function startMemoryTiming(drumTime) { + /* Starts the necessary timers for the memory toggles to aid in their + display on the panels. Note that "drumTime" is in units of word-times */ + + this.updateLampGlow(drumTime); + this.memoryStartTime = drumTime; +}; + +/**************************************/ +B220Processor.prototype.stopMemoryTiming = function stopMemoryTiming() { + /* Stops the active timers for the memory toggles to aid in their + display on the panels and reset the corresponding toggle */ + var drumTime = performance.now()*B220Processor.wordsPerMilli; + var alpha = Math.min((drumTime - this.memoryStartTime)/B220Processor.maxGlowTime, 1.0); + var alpha1 = 1.0 - alpha; + var tg = this.toggleGlow; + + tg.glowMAIN = tg.glowMAIN*alpha1 + this.memMAIN*alpha; + tg.glowRWM = tg.glowRWM*alpha1 + this.memRWM*alpha; + tg.glowRWL = tg.glowRWL*alpha1 + this.memRWL*alpha; + tg.glowWDBL = tg.glowWDBL*alpha1 + this.memWDBL*alpha; + tg.glowACTION = tg.glowACTION*alpha1 + this.memACTION*alpha; + tg.glowACCESS = tg.glowACCESS*alpha1 + this.memACCESS*alpha; + tg.glowLM = tg.glowLM*alpha1 + this.memLM*alpha; + tg.glowL4 = tg.glowL4*alpha1 + this.memL4*alpha; + tg.glowL5 = tg.glowL5*alpha1 + this.memL5*alpha; + tg.glowL6 = tg.glowL6*alpha1 + this.memL6*alpha; + tg.glowL7 = tg.glowL7*alpha1 + this.memL7*alpha; + + if (isNaN(tg.glowMAIN)) {debugger} + + this.memoryStopTime = drumTime; + this.memoryStartTime = + this.memMAIN = + this.memRWM = + this.memRWL = + this.memWDBL = + this.memACTION = + this.memACCESS = + this.memLM = + this.memL4 = + this.memL5 = + this.memL6 = + this.memL7 = 0; +}; + + +/*********************************************************************** +* Bit and Field Manipulation Functions * +***********************************************************************/ + +/**************************************/ +B220Processor.prototype.bitTest = function bitTest(word, bit) { + /* Extracts and returns the specified bit from the word */ + var p; // bottom portion of word power of 2 + + if (bit > 0) { + return ((word - word % (p = B220Processor.pow2[bit]))/p) % 2; + } else { + return word % 2; + } +}; + +/**************************************/ +B220Processor.prototype.bitSet = function bitSet(word, bit) { + /* Sets the specified bit in word and returns the updated word */ + var ue = bit+1; // word upper power exponent + var bpower = // bottom portion of word power of 2 + B220Processor.pow2[bit]; + var bottom = // unaffected bottom portion of word + (bit <= 0 ? 0 : (word % bpower)); + var top = // unaffected top portion of word + word - (word % B220Processor.pow2[ue]); + + return bpower + top + bottom; +}; + +/**************************************/ +B220Processor.prototype.bitReset = function bitReset(word, bit) { + /* Resets the specified bit in word and returns the updated word */ + var ue = bit+1; // word upper power exponent + var bottom = // unaffected bottom portion of word + (bit <= 0 ? 0 : (word % B220Processor.pow2[bit])); + var top = // unaffected top portion of word + word - (word % B220Processor.pow2[ue]); + + return top + bottom; +}; + +/**************************************/ +B220Processor.prototype.bitFlip = function bitFlip(word, bit) { + /* Complements the specified bit in word and returns the updated word */ + var ue = bit+1; // word upper power exponent + var bpower = // bottom portion of word power of 2 + B220Processor.pow2[bit]; + var bottom = // unaffected bottom portion of word + (bit <= 0 ? 0 : (word % bpower)); + var middle = // bottom portion of word starting with affected bit + word % B220Processor.pow2[ue]; + var top = word - middle; // unaffected top portion of word + + if (middle >= bpower) { // if the affected bit is a one + return top + bottom; // return the result with it set to zero + } else { // otherwise + return bpower + top + bottom; // return the result with it set to one + } +}; + +/**************************************/ +B220Processor.prototype.fieldIsolate = function fieldIsolate(word, start, width) { + /* Extracts a bit field [start:width] from word and returns the field */ + var le = start-width+1; // lower power exponent + var p; // bottom portion of word power of 2 + + return (le <= 0 ? word : + (word - word % (p = B220Processor.pow2[le]))/p + ) % B220Processor.pow2[width]; +}; + +/**************************************/ +B220Processor.prototype.fieldInsert = function fieldInsert(word, start, width, value) { + /* Inserts a bit field from the low-order bits of value ([48-width:width]) + into word.[start:width] and returns the updated word */ + var ue = start+1; // word upper power exponent + var le = ue-width; // word lower power exponent + var bpower = // bottom portion of word power of 2 + B220Processor.pow2[le]; + var bottom = // unaffected bottom portion of word + (le <= 0 ? 0 : (word % bpower)); + var top = // unaffected top portion of word + (ue <= 0 ? 0 : (word - (word % B220Processor.pow2[ue]))); + + return (value % B220Processor.pow2[width])*bpower + top + bottom; +}; + +/**************************************/ +B220Processor.prototype.fieldTransfer = function fieldTransfer(word, wstart, width, value, vstart) { + /* Inserts a bit field from value.[vstart:width] into word.[wstart:width] and + returns the updated word */ + var ue = wstart+1; // word upper power exponent + var le = ue-width; // word lower power exponent + var ve = vstart-width+1; // value lower power exponent + var vpower; // bottom port of value power of 2 + var bpower = // bottom portion of word power of 2 + B220Processor.pow2[le]; + var bottom = // unaffected bottom portion of word + (le <= 0 ? 0 : (word % bpower)); + var top = // unaffected top portion of word + (ue <= 0 ? 0 : (word - (word % B220Processor.pow2[ue]))); + + return ((ve <= 0 ? value : + (value - value % (vpower = B220Processor.pow2[ve]))/vpower + ) % B220Processor.pow2[width] + )*bpower + top + bottom; +}; + +/*********************************************************************** +* The 205 Adder and Arithmetic Operations * +***********************************************************************/ + +/**************************************/ +B220Processor.prototype.bcdAdd = function bcdAdd(a, d, complement, initialCarry) { + /* Performs an unsigned, BCD addition of "a" and "d", producing an 11-digit + BCD result. On input, "complement" indicates whether 9s-complement addition + should be performed; "initialCarry" indicates whether an initial carry of 1 + should be applied to the adder. On output, this.togCOMPL will be set from + "complement" and this.CT is set from the final carry toggles of the addition. + Further, this.ADDER will still have a copy of the sign (11th) digit. Sets + the Forbidden-Combination stop if non-decimal digits are encountered, but + does not set the Overflow stop. */ + var ad; // current augend (a) digit; + var adder; // local copy of adder digit + var am = a % 0x100000000000; // augend mantissa + var carry = (initialCarry || 0) & 1;// local copy of carry toggle (CT 1) + var compl = complement || 0; // local copy of complement toggle + var ct = carry; // local copy of carry register (CT 1-16) + var dd; // current addend (d) digit; + var dm = d % 0x100000000000; // addend mantissa + var x; // digit counter + + this.togADDER = 1; // for display only + this.togDPCTR = 1; // for display only + + // Loop through the 11 digits including sign digits + for (x=0; x<11; ++x) { + // shift low-order augend digit right into the adder + ad = am % 0x10; + am = (am - ad)/0x10; + if (ad > 9) { + this.stopForbidden = 1; + this.togCST = 1; // halt the processor + } + + // add the digits plus carry, complementing as necessary + dd = dm % 0x10; + if (dd > 9) { + this.stopForbidden = 1; + this.togCST = 1; // halt the processor + } + if (compl) { + ad = 9-ad; + } + adder = ad + dd + carry; + + // decimal-correct the adder + if (adder < 10) { + carry = 0; + } else { + adder -= 10; + carry = 1; + } + + // compute the carry toggle register (just for display) + ct = (((ad & dd) | (ad & ct) | (dd & ct)) << 1) + carry; + this.updateAdderGlow(adder, ct); + + // rotate the adder into the sign digit + am += adder*0x10000000000; + // shift the addend right to the next digit + dm = (dm - dd)/0x10; + } // for x + + // Set toggles for display purposes and return the result + this.togCOMPL = compl; + this.CT = ct; + this.ADDER = adder; + return am; +}; + +/**************************************/ +B220Processor.prototype.integerAdd = function integerAdd() { + /* Algebraically add the addend (D) to the augend (A), returning the result + in A and clearing D. All values are BCD with the sign in the 11th digit + position. Sets the Overflow and Forbidden-Combination stops as necessary */ + var am = this.A % 0x10000000000; // augend mantissa + var aSign = ((this.A - am)/0x10000000000); + var compl; // complement addition required + var dm = this.D % 0x10000000000; // addend mantissa + var dSign = ((this.D - dm)/0x10000000000); + var sign = dSign & 0x01; // local copy of sign toggle + + this.togADDER = 1; // for display only + this.togDPCTR = 1; // for display only + compl = (aSign^dSign) & 0x01; + am = this.bcdAdd(am, dm, compl, compl); + + // Now examine the resulting sign (still in the adder) to see if we have overflow + // or need to recomplement the result + switch (this.ADDER) { + case 0: + am += sign*0x10000000000; + break; + case 1: + am += (sign-1)*0x10000000000; + this.setOverflow(1); + break; + default: // sign is 9 + // reverse the sign toggle and recomplement the result (virtually adding to the zeroed dm) + sign = 1-sign; + am = this.bcdAdd(am, 0, 1, 1); + // after recomplementing, set the correct sign (adder still contains sign of result) + am += (sign - this.ADDER)*0x10000000000; + this.procTime += 2; + break; + } // switch this.ADDER + + // Set toggles for display purposes and return the result + this.togSIGN = sign; + this.A = am; + this.D = 0; +}; + +/**************************************/ +B220Processor.prototype.integerExtract = function integerExtract() { + /* "Extract" digits from A according to the digit pattern in D. + This is actually a weird form of add. In the simple case, the extract + pattern consists of a string of ones and zeroes. The value digits will be + retained where the corresponding pattern digits are 1 and will be zeroed + where the corresponding pattern digits are zero. + In the general case, if the pattern digit is even, that digit replaces the + corresponding digit in the value. If the pattern digit is odd, then that + digit minus one is added to the corresponding value digit. In this latter + case, carries from digit to digit occur normally, and thus a generalized + extract can result in overflow. + Sets the Overflow and Forbidden-Combination stops as necessary */ + var ad; // current value (A) digit; + var adder = 0; // local copy of adder digit + var am = this.A % 0x10000000000; // value mantissa + var aSign = ((this.A - am)/0x10000000000) & 0x01; + var carry; // local copy of carry toggle (CT 1) + var ct; // local copy of carry register (CT 1-16) + var dd; // current pattern (D) digit; + var dm = this.D; // pattern mantissa + var dSign = ((this.D - this.D%0x10000000000)/0x10000000000); + var sign = aSign & dSign & 0x01; // local copy of sign toggle + var x; // digit counter + + this.togCLEAR = 1; // for display only + this.togADDER = 1; // for display only + this.togDPCTR = 1; // for display only + ct = carry = 0; + + // Loop through the 11 digits including signs (which were set to zero in am and dm) + for (x=0; x<11; ++x) { + // shift low-order value digit right into the adder + ad = am % 0x10; + am = (am - ad)/0x10; + if (ad > 9) { + this.stopForbidden = 1; + this.togCST = 1; // halt the processor + } + + // add the digits plus carry, complementing as necessary + dd = dm % 0x10; + // shift the pattern right to the next digit + dm = (dm - dd)/0x10; + if (dd > 9) { + this.stopForbidden = 1; + this.togCST = 1; // halt the processor + } + if (dd & 0x01) { // if extract digit is odd + dd &= 0x0E; // force extract digit to even (so adder=ad+dd-1) + } else { // otherwise, if it's even + ad = 0; // clear A digit so D digit will replace it (adder=dd) + } + adder = ad + dd + carry; + + // decimal-correct the adder + if (adder < 10) { + carry = 0; + } else { + adder -= 10; + carry = 1; + } + + // compute the carry toggle register (just for display) + ct = (((ad & dd) | (ad & ct) | (dd & ct)) << 1) + carry; + // rotate the adder into the sign digit + am += adder*0x10000000000; + } // for x + + // Now examine the resulting sign (still in the adder) to see if we have overflow + if (adder == 0) { + am += sign*0x10000000000; + } else if (adder == 1) { + am += (sign-1)*0x10000000000; + this.setOverflow(1); + } + + // Set toggles for display purposes and return the result + this.togCOMPL = 0; + this.togSIGN = sign; + this.CT = ct; + this.ADDER = adder; + this.A = am; + this.D = 0; +}; + +/**************************************/ +B220Processor.prototype.integerMultiply = function integerMultiply(roundOff) { + /* Algebraically multiply the D register by the A register, producing a + 20-digit product in A and R. All values are BCD with the sign in the 11th digit + position. If roundOff is truthy, the product in the A register is rounded and + the R register is cleared. Sets Forbidden-Combination stop as necessary. + Overflow is not possible */ + var ad; // current product (A) digit; + var am = this.A % 0x10000000000; // product (A) mantissa + var aSign = ((this.A - am)/0x10000000000) & 0x01; + var count = 0; // count of word-times consumed + var dm = this.D % 0x10000000000; // multiplicand mantissa + var dSign = ((this.D - dm)/0x10000000000) & 0x01; + var rc; // dup of rd for add counting + var rd; // current multipler (R) digit; + var rm = am; // current multiplier (R) mantissa + var sign = aSign ^ dSign; // local copy of sign toggle (sign of product) + var x; // digit counter + + this.togMULDIV = 1; // for display only + this.SHIFT = 0x09; // for display only + this.SHIFTCONTROL = 0x0C; // for display only + am = 0; // clear the local product (A) mantissa + + // We now have the multiplicand in D (dm), the multiplier in R (rm), and an + // initial product of zero in A (am). Go through a classic multiply cycle, + // doing repeated addition based on each multipler digit, and between digits + // shifting the product (in am and rm) one place to the right. After 10 digits, + // we're done. + + this.togCOMPL = 0; + for (x=0; x<10; ++x) { + rc = rd = rm % 0x10; + count += rc; + while (rc > 0) { + am = this.bcdAdd(am, dm, 0, 0); + --rc; + } // while rd + + ad = am % 0x10; + am = (am-ad)/0x10; + rm = (rm-rd)/0x10 + ad*0x1000000000; + } // for x + + if (roundOff) { + ++count; + if (rm >= 0x5000000000) { + am = this.bcdAdd(am, 0x01, 0, 0); + } + this.R = this.D = 0; + } + + this.procTime += 13 + count*2; + this.SPECIAL = 0x09; // for display only + this.togSIGN = sign; // for display only + this.A = sign*0x10000000000 + am; + this.R = rm; + this.D = 0; +}; + +/**************************************/ +B220Processor.prototype.integerDivide = function integerDivide() { + /* Algebraically divide the A & R registers by the D register, producing a + signed 10-digit quotient in A and the remainder in R. All values are BCD + with the sign in the 11th digit position. Sets Forbidden-Combination stop + as necessary. If the magnitude of the divisor (D) is less or equal to the + magnitude of the dividend (A), the Overflow stop it set and division + terminates, unconditionally setting A & R to zero */ + var am = this.A % 0x10000000000; // current remainder (A) mantissa + var aSign = ((this.A - am)/0x10000000000) & 0x01; + var count = 0; // count of word-times consumed + var dm = this.D % 0x10000000000; // divisor mantissa + var dSign = ((this.D - dm)/0x10000000000) & 0x01; + var rd; // current quotient (R) digit; + var rm = this.R; // current quotient (R) mantissa + var sign = aSign ^ dSign; // local copy of sign toggle (sign of quotient) + var x; // digit counter + + this.togMULDIV = 1; // for display only + this.togDELTABDIV = 1; // for display only + this.togDIVALARM = 1; // for display only + this.SPECIAL = 0x09; // for display only: state at end unless overflow is set + + // We now have the divisor in D (dm) and the dividend in A (am) & R (rm). + // The value in am will become the remainder; the value in rm will become + // the quotient. Go through a classic long-division cycle, repeatedly + // subtracting the divisor from the dividend, counting subtractions until + // underflow occurs, and shifting the divisor left one digit. + + for (x=0; x<10; ++x) { + // First, shift A & R to the left one digit, with A1 shifting to ASGN + rd = (rm - rm%0x1000000000)/0x1000000000; + rm = (rm%0x1000000000)*0x10; + am = am*0x10 + rd; + + // Now repeatedly subtract D from A until we would get underflow. + // Unlike the 205, we don't do one subtraction too many. + rd = 0; + while (am >= dm && rd < 10) { + am = this.bcdAdd(dm, am, 1, 1); + ++rd; + ++count; + } + + // Check for overflow (dividend > divisor). + if (rd < 10) { + rm += rd; // accumulate the quotient digit + this.togDIVALARM = 0; + } else { + this.setOverflow(1); + this.SPECIAL = 0x0F; // for display only + am = rm = 0; + break; // out of for loop + } + } // for x + + this.SHIFTCONTROL = 0x0E; // for display only + this.SHIFT = 0x09; // for display only + this.togSIGN = sign; // for display only + this.togSTEP = 1; // for display only + this.A = sign*0x10000000000 + rm; + this.R = am; + this.D = 0; + if (this.stopOverflow) { + this.procTime += 24; + } else { + this.procTime += 52 + count*2; + } +}; + +/**************************************/ +B220Processor.prototype.floatingAdd = function floatingAdd() { + /* Algebraically add the floating-point addend (D) to the floating-point + augend (A), placing the result in A and clearing D. The R register is not + affected. All values are BCD with the sign in the 11th digit position. + The floating exponent is in the first two digit positions, biased by 50. + Sets the Overflow and Forbidden-Combination stops as necessary */ + var ae; // augend exponent (binary) + var am = this.A % 0x10000000000; // augend mantissa (BCD) + var aSign = ((this.A - am)/0x10000000000) & 0x01; + var compl; // complement addition required + var d; // scratch digit; + var de; // addend exponent (binary) + var dm = this.D % 0x10000000000; // addend mantissa (BCD) + var dSign = ((this.D - dm)/0x10000000000) & 0x01; + var sign = dSign; // local copy of sign toggle + + this.togADDER = 1; // for display only + this.togDPCTR = 1; // for display only + + ae = (am - am%0x100000000)/0x100000000; + am %= 0x100000000; + de = (dm - dm%0x100000000)/0x100000000; + dm %= 0x100000000; + + // If the exponents are unequal, normalize the larger and scale the smaller + // until they are in alignment, or one mantissa becomes zero. + if (am == 0) { + ae = de; + } else if (dm == 0) { + de = ae; + } else if (ae > de) { + // Normalize A + while (ae > de && am < 0x10000000) { + am *= 0x10; // shift left + ae = this.bcdAdd(1, ae, 1, 1); // --ae + } + // Scale D until its exponent matches or the mantissa goes to zero. + while (ae > de && dm > 0) { + d = dm % 0x10; + dm = (dm - d)/0x10; // shift right + de = this.bcdAdd(1, de, 0, 0); // ++de + } + } else if (ae < de) { + // Normalize D + while (ae < de && dm < 0x10000000) { + dm *= 0x10; // shift left + de = this.bcdAdd(1, de, 1, 1); // --de + } + // Scale A until its exponent matches or the mantissa goes to zero. + while (ae < de && am > 0) { + d = am % 0x10; + am = (am - d)/0x10; // shift right + ae = this.bcdAdd(1, ae, 0, 0); // ++ae + } + } + + compl = (aSign^dSign); + am = this.bcdAdd(am, dm, compl, compl); + + // Now examine the resulting sign (still in the adder) to see if we + // need to recomplement the result. + if (this.ADDER) { + // Reverse the sign toggle and recomplement the result (virtually adding to the zeroed dm). + sign = 1-sign; + am = this.bcdAdd(am, 0, 1, 1); + this.procTime += 2; + } + + // Normalize or scale the result as necessary + this.SPECIAL = 0; // for display only + if (am >= 0x100000000) { + // Mantissa overflow: add/subtract can produce at most one digit of + // overflow, so shift right and increment the exponent, checking for + // overflow in the exponent. + if (ae < 0x99) { + ++this.SPECIAL; // for display only + d = am % 0x10; + am = (am - d)/0x10; // shift right + ae = this.bcdAdd(1, ae, 0, 0); // ++ae + } else { + // A scaling shift would overflow the exponent, so set the overflow + // alarm and leave the mantissa as it was from the add, without the + // exponent inserted back into it. Since the A register gets reassembled + // below, we need to set up the mantissa and exponent so the reconstruct + // will effectively do nothing. + this.setOverflow(1); + sign = 0; // per 205 FP Handbook + ae = (am - am%0x100000000)/0x100000000; + am %= 0x100000000; + } + } else if (am > 0) { + // Normalize the result as necessary + while (am < 0x10000000) { + if (ae > 0) { + ++this.SPECIAL; // for display only + am *= 0x10; // shift left + ae = this.bcdAdd(1, ae, 1, 1); // --ae + } else { + // Exponent underflow: set R and the reconstructed A to zero. + am = ae = sign = 0; + this.R = 0; + break; + } + } + } else { // mantissa is zero + ae = 0; // per example in 205 FP Handbook + } + + // Set toggles for display purposes and set the result. + this.togSIGN = sign; + this.A = (sign*0x100 + ae)*0x100000000 + am; + this.D = 0; +}; + +/**************************************/ +B220Processor.prototype.floatingMultiply = function floatingMultiply() { + /* Algebraically multiply the floating-point multiplicand in the D register + by the floating-point multiplier in the A register, producing an 18-digit + product (16 mantissa + 2 exponent) in A and R. All values are BCD with the + sign in the 11th digit position. The floating exponent is in the first two + digit positions, biased by 50. Sets the Forbidden-Combination stop as + necessary */ + var ad; // current product (A) digit; + var ae; // product/multiplier (A) exponent + var am = this.A % 0x10000000000; // product (A) mantissa + var aSign = ((this.A - am)/0x10000000000) & 0x01; + var count = 0; // count of word-times consumed + var de; // multiplicand exponent + var dm = this.D % 0x10000000000; // multiplicand mantissa + var dSign = ((this.D - dm)/0x10000000000) & 0x01; + var rc; // dup of rd for add counting + var rd; // current multipler (R) digit; + var rm; // current multiplier (R) mantissa + var sign = aSign ^ dSign; // local copy of sign toggle (sign of product) + var x; // digit counter + + this.togMULDIV = 1; // for display only + this.SPECIAL = 0; // for display only + ae = (am - am%0x100000000)/0x100000000; + am %= 0x100000000; + de = (dm - dm%0x100000000)/0x100000000; + dm %= 0x100000000; + + if (am == 0) { + this.A = this.R = 0; + } else if (dm == 0) { + this.A = this.R = 0; + } else { + ae = this.bcdAdd(ae, de); + if (ae >= 0x150) { + this.setOverflow(1); + sign = 0; + this.A = am; + this.R = 0; + } else { + rm = am; // move the multiplier to R + am = 0; // clear the local product (A) mantissa + this.SHIFT = 0x09; // for display only + this.SHIFTCONTROL = 0x0C; // for display only + + // We now have the multiplicand in D (dm), the multiplier in R (rm), and an + // initial product of zero in A (am). Go through a classic multiply cycle, + // doing repeated addition based on each multipler digit, and between digits + // shifting the product (in am and rm) one place to the right. After 8 digits, + // we're done, except for normalization. + + this.togCOMPL = 0; + for (x=0; x<8; ++x) { + rc = rd = rm % 0x10; + count += rc; + while (rc > 0) { + am = this.bcdAdd(am, dm, 0, 0); + --rc; + } // while rd + + ad = am % 0x10; + am = (am-ad)/0x10; + rm = (rm-rd)/0x10 + ad*0x1000000000; + } // for x + + // Check for exponent underflow + if (ae < 0x50) { + this.A = this.R = 0; // underflow + } else { + // Subtract the exponent bias and normalize the result as necessary. + ae = this.bcdAdd(0x50, ae, 1, 1); + while (am < 0x10000000) { + if (ae <= 0) { + // Exponent underflow: set R and the reconstructed A to zero. + am = ae = sign = 0; + this.R = 0; + break; + } else { + ++this.SPECIAL; // for display only + rd = (rm - rm%0x1000000000)/0x1000000000; + rm = (rm % 0x1000000000)*0x10; + am = am*0x10 + rd; // shift left + ae = this.bcdAdd(1, ae, 1, 1); // --ae + } + } + + this.A = (sign*0x100 + ae)*0x100000000 + am; + this.R = rm; + } + + this.procTime += 13 + count*2; + } + } + + this.togSIGN = sign; // for display only + this.D = 0; +}; + +/**************************************/ +B220Processor.prototype.floatingDivide = function floatingDivide() { + /* Algebraically divide the 18-digit (16 mantissa + 2 exponent) floating- + point dividend in the A & R registers by the floating-point divisor in the + D register, producing a 9- or 10-digit quotient in the A & R registers + and a 6- or 7-digit remainder in the low-order digits of the R register. + See the Floating Point Handbook for the gory details of the result format. + All values are BCD with the sign in the 11th digit position. The floating + exponent is in the first two digit positions, biased by 50. Sets the + Forbidden-Combination stop as necessary */ + var ae; // dividend/quotient exponent + var am = this.A % 0x10000000000; // current remainder (A) mantissa + var aSign = ((this.A - am)/0x10000000000) & 0x01; + var count = 0; // count of word-times consumed + var de; // divisor exponent + var dm = this.D % 0x10000000000; // divisor mantissa + var dSign = ((this.D - dm)/0x10000000000) & 0x01; + var rd; // current quotient (R) digit; + var rm = this.R; // current quotient (R) mantissa + var sign = aSign ^ dSign; // local copy of sign toggle (sign of quotient) + var x; // digit counter + + this.togMULDIV = 1; // for display only + this.togDELTABDIV = 1; // for display only + this.togDIVALARM = 0; // for display only + this.SPECIAL = 0; // for display only + ae = (am - am%0x100000000)/0x100000000; + am %= 0x100000000; + de = (dm - dm%0x100000000)/0x100000000; + dm %= 0x100000000; + + // Normalize A & R + while (am && am < 0x10000000) { + if (ae <= 0) { + am = 0; // exponent underflow + } else { + rd = (rm - rm%0x1000000000)/0x1000000000; + rm = (rm % 0x1000000000)*0x10; + am = am*0x10 + rd; // shift left + ae = this.bcdAdd(1, ae, 1, 1); // --ae + } + } + + // Normalize D + while (dm && dm < 0x10000000) { + if (de <= 0) { + dm = 0; // exponent underflow + } else { + dm *= 0x10; // shift left + de = this.bcdAdd(1, de, 1, 1); // --de + } + } + + // Check for zero operands and commence the division + if (am == 0) { + this.A = this.R = sign = 0; // dividend is zero so result is zero + } else if (dm == 0) { + this.A = this.R = sign = 0; // divide by zero + this.togDIVALARM = 1; // for display only + this.setOverflow(1); + } else { + // Add the exponent bias to the dividend exponent and check for underflow + ae = this.bcdAdd(ae, 0x50); + if (ae < de) { + // Exponents differ by more than 50 -- underflow + this.A = this.R = sign = 0; + } else { + // If dividend >= divisor, scale the exponent by 1 + if (am >= dm) { + ae = this.bcdAdd(ae, 1); + } + // Subtract the exponents and check for overflow + ae = this.bcdAdd(de, ae, 1, 1); + if (ae > 0x99) { + this.setOverflow(1); + sign = 0; + this.A = am; + this.R = rm; + } else { + // We now have the divisor in D (dm) and the dividend in A (am) & R (rm). + // The value in am will become the remainder; the value in rm will become + // the quotient. Go through a classic long-division cycle, repeatedly + // subtracting the divisor from the dividend, counting subtractions until + // underflow occurs, and shifting the divisor left one digit. + + for (x=0; x<10; ++x) { + // Repeatedly subtract D from A until we would get underflow. + // Unlike the 205, we don't do one subtraction too many. + rd = 0; + while (am >= dm) { + am = this.bcdAdd(dm, am, 1, 1); + ++rd; + ++count; + } + + // Shift A & R to the left one digit, accumulating the quotient digit in R + rm = rm*0x10 + rd; + rd = (rm - rm%0x10000000000)/0x10000000000; + rm %= 0x10000000000; + if (x < 9) { + am = am*0x10 + rd; // shift into remainder except on last digit + } + } // for x + + // Rotate the quotient from R into A for 8 digits or until it's normalized + for (x=0; x<8 || am%0x100000000 < 0x10000000; ++x) { + ++this.SPECIAL; // for display only + rd = (am - am%0x1000000000)/0x1000000000; + rm = rm*0x10 + rd; + rd = (rm - rm%0x10000000000)/0x10000000000; + rm %= 0x10000000000; + am = (am%0x1000000000)*0x10 + rd; + } + + this.SHIFTCONTROL = 0x0E; // for display only + this.SHIFT = 0x09; // for display only + this.togSTEP = 1; // for display only + + this.A = (sign*0x100 + ae)*0x100000000 + am%0x100000000; + this.R = rm; + } + + if (this.stopOverflow) { + this.procTime += 24; + } else { + this.procTime += 52 + count*2; + } + } + } + + this.togSIGN = sign; // for display only + this.D = 0; +}; + + +/*********************************************************************** +* Memory Access * +***********************************************************************/ + +/**************************************/ +B220Processor.prototype.readMemoryFinish = function readMemoryFinish() { + /* Completes a read of the memory drum after an appropriate delay for drum + latency. Clears the memory control toggles and calls the current successor + function. Note: the word has already been stored in D by readMemory() */ + + this.scheduler = 0; + this.stopMemoryTiming(); + this.successor(); +}; + +/**************************************/ +B220Processor.prototype.readMemory = function readMemory(successor) { + /* Initiates a read of the memory drum at the BCD address specified by + C3-C6. The memory word is placed in the D register, and an appropriate + delay for drum latency, the successor function is called. Sets the memory + access toggles as necessary. + Note: the D register SHOULD be loaded after the latency delay, but we do + it here so that the word will show on the panels during the drum latency */ + var addr; // binary target address, mod 8000 + var cbCategory; // setCallback delay-averaging category + var drumTime = // current drum position [word-times] + performance.now()*B220Processor.wordsPerMilli; + var latency; // drum latency in word-times + var trackSize; // words/track in target band of drum + + addr = B220Processor.bcdBinary(this.CADDR % 0x8000); + this.memACCESS = 1; + if (addr < 4000) { + trackSize = B220Processor.trackSize; + cbCategory = "MEMM"; + this.memMAIN = this.memLM = 1; + this.D = this.MM[addr]; + } else { + trackSize = B220Processor.loopSize; + cbCategory = "MEML"; + if (addr < 5000) { + this.memL4 = 1; + this.D = this.L4[addr%trackSize]; + } else if (addr < 6000) { + this.memL5 = 1; + this.D = this.L5[addr%trackSize]; + } else if (addr < 7000) { + this.memL6 = 1; + this.D = this.L6[addr%trackSize]; + } else { + this.memL7 = 1; + this.D = this.L7[addr%trackSize]; + } + } + + latency = (addr%trackSize - drumTime%trackSize + trackSize)%trackSize; + this.procTime += latency+1; // emulated time at end of drum access + this.memACTION = 1; + this.startMemoryTiming(drumTime); + this.successor = successor; + this.scheduler = setCallback(cbCategory, this, + (this.procTime-drumTime)/B220Processor.wordsPerMilli, this.readMemoryFinish); +}; + +/**************************************/ +B220Processor.prototype.writeMemoryFinish = function writeMemoryFinish(clearA) { + /* Completes a write of the memory drum after an appropriate delay for drum + latency. A has already been moved to the memory word, so just clears the + memory control toggles, and calls the current successor function */ + + this.scheduler = 0; + this.stopMemoryTiming(); + if (clearA) { + this.A = 0; + } + this.successor(); +}; + +/**************************************/ +B220Processor.prototype.writeMemory = function writeMemory(successor, clearA) { + /* Initiates a write of the memory drum at the BCD address specified by C3-C6. + After an appropriate delay for drum latency, the word in A is stored on the drum + and the successor function is called. Sets the memory access toggles as necessary. + If clearA is truthy, the A register is cleared at the completion of the write. + Note: the word should be stored after the latency delay, but we do it here + so that the word will show in the panels during the drum latency */ + var addr; // binary target address, mod 8000 + var cbCategory; // setCallback delay-averaging category + var drumTime = // current drum position [word-times] + performance.now()*B220Processor.wordsPerMilli; + var latency; // drum latency in word-times + var trackSize; // words/track in target band of drum + + addr = B220Processor.bcdBinary(this.CADDR % 0x8000); + this.memACCESS = 1; + if (addr < 4000) { + trackSize = B220Processor.trackSize; + cbCategory = "MEMM"; + this.memMAIN = this.memLM = this.memRWM = 1; + this.MM[addr] = this.A; + } else { + trackSize = B220Processor.loopSize; + cbCategory = "MEML"; + this.memRWL = 1; + if (addr < 5000) { + this.memL4 = 1; + this.L4[addr%trackSize] = this.A; + } else if (addr < 6000) { + this.memL5 = 1; + this.L5[addr%trackSize] = this.A; + } else if (addr < 7000) { + this.memL6 = 1; + this.L6[addr%trackSize] = this.A; + } else { + this.memL7 = 1; + this.L7[addr%trackSize] = this.A; + } + } + + latency = (addr%trackSize - drumTime%trackSize + trackSize)%trackSize; + this.procTime += latency+1; // emulated time at end of drum access + this.memACTION = 1; + this.startMemoryTiming(drumTime); + this.successor = successor; + this.scheduler = setCallback(cbCategory, this, + (this.procTime-drumTime)/B220Processor.wordsPerMilli, this.writeMemoryFinish, clearA); +}; + +/**************************************/ +B220Processor.prototype.blockFromLoop = function blockFromLoop(loop, successor) { + /* Copies 20 words from the designated loop to main memory at the BCD + address specified by C3-C6. After an appropriate delay for drum latency, + the successor function is called. Sets the memory access toggles as + necessary. + Note: the words SHOULD be copied at the END of the drum latency delay, + but we do it here, so that the updated registers will show on the panels + during the delay for drum latency */ + var addr; // main binary address, mod 4000 + var drumTime = // current drum position [word-times] + performance.now()*B220Processor.wordsPerMilli; + var latency; // drum latency in word-times + var loopMem; // reference to the loop memory array + var x; // iteration control + + addr = B220Processor.bcdBinary(this.CADDR % 0x4000); + this.memACCESS = 1; + this.memMAIN = this.memLM = this.memRWM = this.memWDBL = 1; + switch (loop) { + case 4: + this.memL4 = 1; + loopMem = this.L4; + break; + case 5: + this.memL5 = 1; + loopMem = this.L5; + break; + case 6: + this.memL6 = 1; + loopMem = this.L6; + break; + case 7: + this.memL7 = 1; + loopMem = this.L7; + break; + } // switch loop + + for (x=B220Processor.loopSize; x>0; --x) { + this.MM[addr] = loopMem[addr%B220Processor.loopSize]; + addr = (addr+1) % 4000; // handle main memory address wraparound + } + + /* According to TM4001, the memory control circuits added 200 to the C-register + address, but only if the blocking operation crossed a main-memory track boundary. + The Burroughs 205 Handbook of Operating Procedures (Bulletin 2034-A, Rev June 1960), + however, indicates that for systems equipped with magnetic tape, blocking operations + added 20 to the address field in C (see paragraph 3-19 on page 3-2). We implement + the magnetic tape variant */ + + this.CADDR = this.bcdAdd(this.CADDR, 0x20)%0x10000; + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + + latency = (addr%B220Processor.trackSize - drumTime%B220Processor.trackSize + + B220Processor.trackSize)%B220Processor.trackSize; + this.procTime += latency+B220Processor.loopSize; // emulated time at end of drum access + this.memACTION = 1; + this.startMemoryTiming(drumTime); + this.successor = successor; + this.scheduler = setCallback("MEMM", this, + (this.procTime-drumTime)/B220Processor.wordsPerMilli, this.writeMemoryFinish, false); +}; + +/**************************************/ +B220Processor.prototype.blockToLoop = function blockToLoop(loop, successor) { + /* Copies 20 words from the main memory at the BCD address specified by + C3-C6 to the designated loop. After an appropriate delay for drum latency, + the successor function is called. Sets the memory access toggles as + necessary. + Note 1: Knuth's MEASY assembler and the Shell assembler both use instructions + of the form BTn 8000. Normally, this would mean an effective address of 0000, + but considering the special use of addresses >= 8000 for magnetic tape + instructions, and the apparent intention of this use in both assemblers, + we conclude that BTn with the high-order bit of the address set causes 20 + words of zeroes to be written to the designated loop. + Note 2: the words SHOULD be copied at the END of the drum latency delay, + but we do it here, so that the updated registers will show on the panels + during the delay for drum latency */ + var addr; // main binary address, mod 4000 + var drumTime = // current drum position [word-times] + performance.now()*B220Processor.wordsPerMilli; + var latency; // drum latency in word-times + var loopMem; // reference to the loop memory array + var x; // iteration control + + addr = B220Processor.bcdBinary(this.CADDR); + this.memACCESS = 1; + this.memLM = this.memRWL = this.memWDBL = 1; + if (addr < 8000) { + addr %= 4000; + this.memMAIN = 1; + } + switch (loop) { + case 4: + this.memL4 = 1; + loopMem = this.L4; + break; + case 5: + this.memL5 = 1; + loopMem = this.L5; + break; + case 6: + this.memL6 = 1; + loopMem = this.L6; + break; + case 7: + this.memL7 = 1; + loopMem = this.L7; + break; + } // switch loop + + if (addr >= 8000) { + for (x=B220Processor.loopSize; x>0; --x) { + loopMem[x] = 0; + } + } else { + for (x=B220Processor.loopSize; x>0; --x) { + loopMem[addr%B220Processor.loopSize] = this.MM[addr]; + addr = (addr+1) % 4000; // handle main memory address wraparound + } + } + + // See the comment on C address field adjustment at this point in blockFromLoop() + this.CADDR = this.bcdAdd(this.CADDR, 0x20)%0x10000; + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + + latency = (addr%B220Processor.trackSize - drumTime%B220Processor.trackSize + + B220Processor.trackSize)%B220Processor.trackSize; + this.procTime += latency+B220Processor.loopSize; // emulated time at end of drum access + this.memACTION = 1; + this.startMemoryTiming(drumTime); + this.successor = successor; + this.scheduler = setCallback("MEMM", this, + (this.procTime-drumTime)/B220Processor.wordsPerMilli, this.writeMemoryFinish, false); +}; + +/**************************************/ +B220Processor.prototype.searchMemoryFinish = function searchMemoryFinish() { + /* Handles completion of a search memory operation and resets the memory + toggle and timing state */ + + this.stopMemoryTiming(); + this.executionComplete(); +}; + +/**************************************/ +B220Processor.prototype.searchMemory = function searchMemory(high) { + /* Searches memory from the current operand address in this.CADDR for a + word equal to the value in the A register (high=false) or greater than or + equal to the value in the A register (high=true). Comparisons are based on + the absolute values of both the word in A and the word in memory, ignoring + the sign. If a match is found, load the memory word into the A register and + set the upper 4 digits of R to the memory address where found. If address + 3999 is reached and the word is still not found, terminate with Overflow set. + + This instruction searches one word per word-time, so total execution time is + drum latency to the initial address plus the number of words searched. + This function is called after the initial memory word has been fetched into + the D register by executeWithOperand(), so the latency delay is already + accounted for. It will proceed to search up to 200 words (one drum track) at + a time, then if no match is found, perform a catch-up delay. A final partial + delay is performed at the end of the search. + + Note: the MSH (85) and MSE (88) instructions apparently were a custom + modification in 1959 for a 205 to be delivered to the Eaton Manufacturing + Company. A description of these two instructions was found in the back of + the 205 Central Computer Handbook included with Donald Knuth's papers donated + to the Computer History Museum in Mountain View, California */ + var addr; // main binary address, mod 4000 + var aWord = this.A % 0x10000000000; // search target word + var drumTime; // current drum position [word-times] + var dWord; // result of comparison D:A + var found = false; // true if matching word found + var x = 0; // iteration control + + if (!this.memACCESS) { + this.memACCESS = this.memACTION = this.memMAIN = this.memLM = 1; + this.startMemoryTiming(performance.now()*B220Processor.wordsPerMilli); + } + + addr = B220Processor.bcdBinary(this.CADDR % 0x4000); + do { + dWord = this.bcdAdd(aWord, this.D % 0x10000000000, 1, 1); // effectively abs(D)-abs(A) + if (dWord == 0) { + found = true; + break; // out of do loop -- match equal + } else if (high && dWord < 0x10000000000) { + found = true; + break; // out of do loop -- match high + } else if (addr < 3999) { + this.D = this.MM[++addr]; + } else { + this.setOverflow(1); + break; // out of do loop -- end of main memory + } + } while (++x < B220Processor.trackSize); + + this.CADDR = B220Processor.binaryBCD(addr); + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + if (found) { + this.A = this.D; + this.R = this.CADDR*0x1000000 + this.R%0x1000000; + this.successor = this.searchMemoryFinish; + } else if (this.stopOverflow) { + this.successor = this.searchMemoryFinish; + } else { + this.successor = searchMemory; + } + + this.procTime += x; + drumTime = performance.now()*B220Processor.wordsPerMilli; + this.scheduler = setCallback("MEML", this, + (this.procTime-drumTime)/B220Processor.wordsPerMilli, this.successor, high); +}; + +/*********************************************************************** +* Console I/O Module * +***********************************************************************/ + +/**************************************/ +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 */ + var d; + var w = this.A % 0x10000000000; + + if (this.togPO1) { // if false, we've probably been cleared + d = (this.A - w)/0x10000000000; // get the digit + this.A = 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(); + } 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); + } + } +}; + +/**************************************/ +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 + output operation and sends a Finish signal */ + var d; + var w = this.A % 0x10000000000; + + 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); + } else { + d = (this.A - w)/0x10000000000; // get the digit + this.A = w*0x10 + d; // rotate A+sign left one + this.SHIFT = this.bcdAdd(this.SHIFT, 1); + this.console.writeNumberDigit(d, this.boundConsoleOutputNumberDigit); + } + } +}; + +/**************************************/ +B220Processor.prototype.consoleOutputFinished = function consoleOutputFinished() { + /* Handles the final cycle of an I/O operation and restores this.procTime */ + + 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.procTime += performance.now()*B220Processor.wordsPerMilli; + this.executeComplete(); + } +}; + +/**************************************/ +B220Processor.prototype.consoleInputDigit = function consoleInputDigit() { + // Solicits the next input digit from the Control Console */ + + this.togTF = 0; // for display only, reset finish pulse + this.procTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O + this.console.readDigit(this.boundConsoleReceiveDigit); +}; + +/**************************************/ +B220Processor.prototype.consoleReceiveDigit = function consoleReceiveDigit(digit) { + /* Handles an input digit coming from the Control Console keyboard or + paper-tape reader. Negative values indicate a finish pulse; otherwise + the digit is data read from the device. Data digits are rotated into + the D register; finish pulses are handled according to the sign digit + in the D register */ + var sign; // register sign digit + var word; // register word less sign + + this.procTime += performance.now()*B220Processor.wordsPerMilli; // restore time after I/O + if (digit >= 0) { + this.togTC1 = 1-this.togTC1; // for display only + this.togTC2 = 1-this.togTC2; // for display only + this.D = (this.D % 0x10000000000)*0x10 + digit; + this.consoleInputDigit(); + } else { + this.togTF = 1; + this.togTC1 = this.togTC2 = 0; // for display only + word = this.D%0x10000000000; + sign = (this.D - word)/0x10000000000; // get D-sign + + if (sign & 0x04) { + // D-sign is 4, 5, 6, 7: execute a "tape control" command + this.procTime += 2; + this.togTF = 0; // for display only + this.togSTART = 1-((sign >>> 1) & 0x01); // whether to continue in input mode + this.setTimingToggle(0); // Execute mode + this.togCOUNT = 1; + this.togBTOAIN = 0; + this.togADDAB = 1; // for display only + this.togADDER = 1; // for display only + this.togDPCTR = 1; // for display only + this.togCLEAR = 1-(sign & 0x01); + this.togSIGN = ((this.A - this.A%0x10000000000)/0x10000000000) & 0x01; // display only + sign &= 0x08; + + // Increment the destination address (except on the first word) + this.SHIFTCONTROL = 0x01; // for display only + this.SHIFT = 0x13; // for display only + if (this.togCOUNT) { + this.CCONTROL = this.bcdAdd(this.CADDR, 1)%0x10000; + } + + 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 + this.D = sign*0x100000 + (word - word%0x1000000)/0x1000000; + if (this.togCLEAR) { + word = this.bcdAdd(word%0x1000000, 0); + } else { + word = this.bcdAdd(word%0x1000000, this.B) % 0x1000000; + } + this.SHIFT = 0x19; // for display only + this.C = word*0x10000 + this.CCONTROL; // put C back together + this.CADDR = word % 0x10000; + this.COP = (word - this.CADDR)/0x10000; + this.execute(); + } else { + // D-sign is 0, 1, 2, 3: store word, possibly modified by B + this.procTime += 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.togCLEAR = 1-((sign >>> 1) & 0x01); + this.togSIGN = sign & 0x01; + + // 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)%0x10000; + } + this.CCONTROL = this.CADDR; + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + + // Modify the word by B as necessary and store it + this.D = (sign & 0x0C)*0x10000000000 + word; + if (this.togCLEAR) { + this.A = this.bcdAdd(this.D, 0); + } else { + this.A = this.bcdAdd(this.D, this.B); + } + + this.D = 0; + word = this.A % 0x10000000000; + sign = (((this.A - word)/0x10000000000) & 0x0E) | (sign & 0x01); + this.A = sign*0x10000000000 + word; + this.writeMemory(this.boundConsoleInputDigit, false); + } + } +}; + +/**************************************/ +B220Processor.prototype.consoleReceiveSingleDigit = function consoleReceiveSingleDigit(digit) { + /* Handles a single input digit coming from the Control Console keyboard + or paper-tape reader, as in the case of Digit Add (10). Negative values + indicate a finish pulse, which is ignored, and causes another digit to be + solicited from the Console; otherwise the digit is (virtually) moved to + the D register and then (actually) added to the A register */ + var sign; // register sign digit + var word; // register word less sign + + if (digit < 0) { // ignore finish pulse and just re-solicit + this.console.readDigit(this.boundConsoleReceiveSingleDigit); + } else { + this.procTime += performance.now()*B220Processor.wordsPerMilli + 4; // restore time after I/O + this.togSTART = 0; + this.D = digit; + this.integerAdd(); + this.executeComplete(); + } +}; + +/*********************************************************************** +* Cardatron I/O Module * +***********************************************************************/ + +/**************************************/ +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 = this.D; // move D with the memory word to A + this.togTWA = 1; // for display only + this.procTime -= performance.now()*B220Processor.wordsPerMilli; + this.ioCallback(this.A, this.boundCardatronOutputWord, this.boundCardatronOutputFinished); + } +}; + +/**************************************/ +B220Processor.prototype.cardatronOutputWord = function cardatronOutputWord(receiver) { + /* Initiates a read of the next word from memory for output to the + Cardatron Control Unit */ + + this.procTime += 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)%0x10000; + } else { + this.togCOUNT = 1; + } + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + this.ioCallback = receiver; + this.readMemory(this.boundCardatronOutputWordReady); + } +}; + +/**************************************/ +B220Processor.prototype.cardatronOutputFinished = function cardatronOutputFinished() { + /* Handles the final cycle of an I/O operation and restores this.procTime */ + + if (this.tog3IO) { // if false, we've probably been cleared + this.tog3IO = 0; + this.togTWA = 0; // for display only + this.procTime += performance.now()*B220Processor.wordsPerMilli; + this.executeComplete(); + } +}; + +/**************************************/ +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.procTime -= 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 */ + var sign; // D-register sign digit + + this.procTime += performance.now()*B220Processor.wordsPerMilli; // restore time after I/O + 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 = word-900000000000; // remove the finished signal; for display only, not stored + this.executeComplete(); + } 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 = word; + word %= 0x10000000000; // strip the sign digit + sign = (this.D - word)/0x10000000000; // get D-sign + + if (sign & 0x04) { + // D-sign is 4, 5, 6, 7: execute the word as an instruction + this.procTime += 2; + this.togTF = 0; // for display only + this.togSTART = 1-((sign >>> 1) & 0x01); // 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 & 0x01)); + this.togSIGN = ((this.A - this.A%0x10000000000)/0x10000000000) & 0x01; // 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); + } else { + word = this.bcdAdd(word%0x1000000, this.B) % 0x1000000; + } + this.C = 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 = 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.procTime += 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 & 0x01; + if (this.kDigit & 0x08) { + this.togCLEAR = 1; + } else { + this.togCLEAR = 1-((sign >>> 1) & 0x01); + sign &= 0x0D; + } + + // 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)%0x10000; + } + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + + // Modify the word by B as necessary and store it + if (this.togCLEAR) { + word = this.bcdAdd(word, 0); + } else { + word = this.bcdAdd(word, this.B); + } + + this.A = sign*0x10000000000 + word%0x10000000000; + this.SHIFT = 0x09; // reset shift counter for next word + this.D = 0; // clear D and request the next word after storing this one + this.writeMemory(this.boundCardatronInputWord, false); + } + } +}; + +/*********************************************************************** +* Magnetic Tape I/O Module * +***********************************************************************/ + +/**************************************/ +B220Processor.prototype.magTapeInitiateSend = function magTapeInitiateSend(writeInitiate) { + /* Performs the initial memory block-to-loop operation after the tape control + unit has determined the drive is ready and not busy. Once the initial loop is + loaded, calls "writeInitiate" to start tape motion, which in turn will cause + the control to call this.magTapeSendBlock to pass the loop data to the drive + and initiate loading of the alternate loop buffer */ + var that = this; + + if (this.togMT3P) { // if false, we've probably been cleared + if (this.CADDR >= 0x8000) { + writeInitiate(this.boundMagTapeSendBlock, this.boundMagTapeTerminateSend); + } else { + this.procTime += performance.now()*B220Processor.wordsPerMilli; // restore time after I/O + this.blockToLoop((this.togMT1BV4 ? 4 : 5), function initialBlockComplete() { + that.procTime -= performance.now()*B220Processor.wordsPerMilli; // suspend time during I/O + writeInitiate(that.boundMagTapeSendBlock, that.boundMagTapeTerminateSend); + }); + } + } +}; + +/**************************************/ +B220Processor.prototype.magTapeSendBlock = function magTapeSendBlock(lastBlock) { + /* Sends a block of data from a loop buffer to the tape control unit and + initiates the load of the alternate loop buffer. this.togMT1BV4 and + this.togMT1BV5 control alternation of the loop buffers. "lastBlock" indicates + this will be the last block requested by the control unit and no further + blocks should be buffered. If the C-register address is 8000 or higher, the + loop is not loaded from main memory, and the current contents of the loop + are written to tape. Since tape block writes take 46 ms, they are much + longer than any memory-to-loop transfer, so this routine simply exits after + the next blockToLoop is initiated, and the processor then waits for the tape + control unit to request the next block, by which time the blockToLoop will + have completed. Returns null if the processor has been cleared and the I/O + must be aborted */ + var loop; + var that = this; + + function blockFetchComplete() { + that.procTime -= performance.now()*B220Processor.wordsPerMilli; // suspend time again during I/O + } + + //console.log("TSU " + this.selectedUnit + " W, L" + (this.togMT1BV4 ? 4 : 5) + + // ", ADDR=" + this.CADDR.toString(16) + + // " : " + block[0].toString(16) + ", " + block[19].toString(16)); + + if (!this.togMT3P) { + loop = null; + } else { + // Select the appropriate loop to send data to the drive + if (this.togMT1BV4) { + loop = this.L4; + this.toggleGlow.glowL4 = 1; // turn on the lamp and let normal decay work + } else { + loop = this.L5; + this.toggleGlow.glowL5 = 1; + } + + if (!lastBlock) { + this.procTime += performance.now()*B220Processor.wordsPerMilli; // restore time after I/O + // Flip the loop-buffer toggles + this.togMT1BV5 = this.togMT1BV4; + this.togMT1BV4 = 1-this.togMT1BV4; + // Block the loop buffer from main memory if appropriate + if (this.CADDR < 0x8000) { + this.blockToLoop((this.togMT1BV4 ? 4 : 5), blockFetchComplete); + } else { + blockFetchComplete(); + } + } + + this.A = loop[loop.length-1]; // for display only + this.D = 0; // for display only + } + + return loop; // give the loop data to the control unit +}; + +/**************************************/ +B220Processor.prototype.magTapeTerminateSend = function magTapeTerminateSend() { + /* Called by the tape control unit after the last block has been completely + written to tape. Terminates the write instruction */ + + if (this.togMT3P) { // if false, we've probably been cleared + this.togMT3P = 0; + this.togMT1BV4 = this.togMT1BV5 = 0; + this.procTime += performance.now()*B220Processor.wordsPerMilli; // restore time after I/O + this.executeComplete(); + } +}; + +/**************************************/ +B220Processor.prototype.magTapeReceiveBlock = function magTapeReceiveBlock(block, lastBlock) { + /* Called by the tape control unit to store a block of 20 words. If "lastBlock" is + true, it indicates this is the last block and the I/O is finished. If "block" + is null, that indicates the I/O was aborted and the block must not be stored + in memory. The block is stored in one of the loops, as determined by the + togMT1BV4 and togMT1BV5 control toggles. Sign digit adjustment and B-register + modification take place at this time. If the C-register operand address is + less than 8000, the loop is then stored at the current operand address, which + is incremented by blockFromLoop(). If this is the last block, executeComplete() + is called after the loop is stored to terminate the read instruction. Since + tape block reads take 46 ms, they are much longer than any loop-to-memory + transfer, so this routine simply exits after the blockFromLoop is initiated, + and the then processor waits for the next block to arrive from the tape, by + which time the blockFromLoop will (should?) have completed. Returns true if + the processor has been cleared and the tape control unit should abort the I/O */ + var aborted = false; // return value + var loop; + var sign; // sign digit + var that = this; + var w; // scratch word + var x; // scratch index + + function blockStoreComplete() { + if (lastBlock) { + if (that.togMT3P) { // if false, we've probably been cleared + that.A = that.D = 0; // for display only + that.togMT3P = 0; + that.togMT1BV4 = that.togMT1BV5 = 0; + that.executeComplete(); + } + } else { + // Flip the loop buffer toggles + that.togMT1BV5 = that.togMT1BV4; + that.togMT1BV4 = 1-that.togMT1BV4; + // Suspend time again during I/O + that.procTime -= performance.now()*B220Processor.wordsPerMilli; + } + } + + //console.log("TSU " + this.selectedUnit + " R, L" + (this.togMT1BV4 ? 4 : 5) + + // ", ADDR=" + this.CADDR.toString(16) + + // " : " + block[0].toString(16) + ", " + block[19].toString(16)); + + if (!this.togMT3P) { // if false, we've probably been cleared + aborted = true; + } else { + this.procTime += performance.now()*B220Processor.wordsPerMilli; // restore time after I/O + // Select the appropriate loop to receive data from the drive + if (this.togMT1BV4) { + loop = this.L4; + this.toggleGlow.glowL4 = 1; // turn on the lamp and let normal decay work + } else { + loop = this.L5; + this.toggleGlow.glowL5 = 1; + } + + if (!block) { // control unit aborted the I/O + blockStoreComplete(); + } else { + // Copy the tape block data to the appropriate high-speed loop + for (x=0; x=0; --x) { + d = (w - w%0x1000000000)/0x1000000000; + w = (w%0x1000000000)*0x10; + switch (d%0x04) { + case 1: // open the switch + this.externalSwitch[x] = 0; + break; + case 2: // close the switch + this.externalSwitch[x] = 1; + break; + case 3: // complement the switch + this.externalSwitch[x] = 1 - (this.externalSwitch[x] % 0x01); + break; + } // switch + } // for x +}; + + +/*********************************************************************** +* Fetch Module * +***********************************************************************/ + +/**************************************/ +B220Processor.prototype.transferDtoC = function transferDtoC() { + /* Implements the D-to-C portion of the fetch cycle. Shifts the operand + address in C3-C6 to C7-C10, incrementing that address by one. Shifts the + low-order six digits of D into C1-C6, applying B register modification if + the D sign bit is 1. Leaves D shifted six digits to the right. + Note that during B modification, overflow may occur from the operand address + into the opcode digits coming from D, which on the 205 could be either a + feature or a bug, depending on how well you were paying attention */ + var addr = this.C % 0x100000000; // operand address digits from C + var ctl = addr % 0x10000; // control address digits from C + + this.SHIFTCONTROL = 0x07; // for display only + this.togADDAB = 1; // for display only + this.togCLEAR = // determine D sign for B modification + 1-(((this.D - this.D%0x10000000000)/0x10000000000) & 0x01); + + addr = (addr-ctl)/0x10000; // extract the actual address digits + this.CCONTROL = this.bcdAdd(addr, 0x0001) % 0x10000;// bump the control address modulo 10000 + + addr = this.D % 0x1000000; // get the opcode and address digits from D + this.CEXTRA = this.D = (this.D - addr)/0x1000000; // shift D right six digits & save what's left + if (this.togCLEAR) { // check for B modification + addr = this.bcdAdd(addr, 0); // if no B mod, add 0 (this is actually the way it worked) + } else { + addr = this.bcdAdd(addr, this.B) % 0x1000000; // otherwise, add B to the operand address + } + + this.SHIFT = 0x19; // for display only + this.C = addr*0x10000 + this.CCONTROL; // put C back together + this.CADDR = addr % 0x10000; + this.COP = (addr - this.CADDR)/0x10000; +}; + +/**************************************/ +B220Processor.prototype.fetchComplete = function fetchComplete() { + /* Second phase of the Fetch cycle, called after the word is read from + memory into D */ + var breakDigit; // breakpoint digit from D4 + + this.togSIGN = ((this.A - this.A%0x10000000000)/0x10000000000) & 0x01; + this.transferDtoC(); + this.procTime += 4; // minimum alpha for all orders is 4 + breakDigit = this.CEXTRA % 0x10; + if (this.cswBreakpoint & breakDigit) { // check for breakpoint stop + this.togBKPT = 1; // prepare to halt after this instruction + } + + if (this.cswSkip && (breakDigit & 0x08)) { + if (!this.sswLockNormal) { + this.setTimingToggle(1); // stay in Fetch to skip this instruction + } + if (breakDigit & 0x01) { + this.togCST = 1; // halt the processor + } + } + + // If we're not halted and either console has started in Continuous mode, continue + if (this.togCST || !(this.sswStepContinuous || this.cctContinuous)) { + this.stop(); + } else if (this.togTiming) { + this.fetch(); // once more with feeling + } else { + this.execute(); // execute the instruction just fetched + } +}; + +/**************************************/ +B220Processor.prototype.fetch = function fetch() { + /* Implements the Fetch cycle of the 205 processor. This is initiated either + by pressing START on one of the consoles with the Timing Toggle=1 (Fetch), + or by the prior operation complete if the processor is in continuous mode */ + + this.CADDR = this.C % 0x100000000; // C operand and control addresses + this.CCONTROL = this.CADDR % 0x10000; // C control address + this.COP = (this.C - this.CADDR)/0x100000000; // C operation code + this.CADDR = (this.CADDR - this.CCONTROL)/0x10000; + + this.togT0 = 0; // for display only, leave it off for fetch cycle + if (this.togSTART) { + if (this.tog3IO) { + this.cardatronInputWord(); // we're still executing a Cardatron input command + } else { + this.consoleInputDigit(); // we're still executing a Console input command + } + } else { + this.setTimingToggle(0); // next cycle will be Execute by default + this.SHIFT = 0x15; // for display only + this.SHIFTCONTROL = 0x05; // for display only + // shift control address into operand address and initiate read + this.CADDR = this.CCONTROL; + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + this.readMemory(this.fetchComplete); // load D from the operand address + } +}; + +/*********************************************************************** +* Execute Module * +***********************************************************************/ + +/**************************************/ +B220Processor.prototype.executeComplete = function executeComplete() { + /* Implements Operation Complete (O.C.) for the Execute cycle. Determines + if there is a stop or alarm condition, otherwise determines whether to + do a Fetch or Execute cycle next. We should clear the control toggles here, + but leave them so they'll display during the fetch latency delay. They + will be cleared in the next call to execute() */ + + if (this.togBKPT) { + this.togCST = 1; + this.stopBreakpoint = 1; // turn on BKPT lamp + } + + if (this.togCST) { + this.stop(); + } else if (!(this.sswStepContinuous || this.cctContinuous)) { + this.stop(); + } else if (this.togTiming) { + this.fetch(); // once more with feeling + } else { + this.execute(); // execute the instruction currently in C + } +}; + +/**************************************/ +B220Processor.prototype.executeWithOperand = function executeWithOperand() { + /* Executes an instruction that requires an operand, after that operand + has been read from memory into the D register */ + + switch (this.COP) { + case 0x60: //---------------- M Multiply + this.integerMultiply(false); + break; + + case 0x61: //---------------- Div Divide + this.integerDivide(); + break; + + // 0x62: //---------------- (no op) + break; + + case 0x63: //---------------- EX Extract + this.procTime += 3; + this.integerExtract(); + break; + + case 0x64: //---------------- CAD Clear and Add A + this.procTime += 3; + this.A = this.bcdAdd(0, this.D); + this.D = 0; + break; + + case 0x65: //---------------- CSU Clear and Subtract A + this.procTime += 3; + // Complement the D sign -- any sign overflow will be ignored by integerAdd + this.A = this.bcdAdd(0, this.bitFlip(this.D, 40)); + this.D = 0; + break; + + case 0x66: //---------------- CADA Clear and Add Absolute + this.procTime += 3; + this.A = this.bcdAdd(0, this.bitReset(this.D, 40)); + this.D = 0; + break; + + case 0x67: //---------------- CSUA Clear and Subtract Absolute + this.procTime += 3; + this.A = this.bcdAdd(0, this.bitSet(this.D, 40)); + this.D = 0; + break; + + // 0x68-0x69: //---------------- (no op) + + case 0x70: //---------------- MRO Multiply and Round + this.integerMultiply(true); + break; + + case 0x71: //---------------- EXC External Control + this.setExternalSwitches(); + break; + + case 0x72: //---------------- SB Set B + this.procTime += 3; + this.SHIFT = 0x15; // for display only + this.SHIFTCONTROL = 0; // for display only + this.togADDAB = 1; // for display only + this.togCLEAR = 1; // for display only + this.DELTABDIVT = 1; // for display only + this.B = this.bcdAdd(0, this.D % 0x10000); + this.togSIGN = ((this.A - this.A%0x10000000000)/0x10000000000) & 0x01; // for display only + this.D = 0; + break; + + case 0x73: //---------------- OSGD Overflow on Sign Difference + this.procTime += 2; + this.togSIGN = ((this.A - this.A%0x10000000000)/0x10000000000) & 0x01; // for display, mostly + this.setOverflow(this.togSIGN ^ + (((this.D - this.D%0x10000000000)/0x10000000000) & 0x01)); + this.D = 0; + break; + + case 0x74: //---------------- AD Add + this.procTime += 3; + this.integerAdd(); + break; + + case 0x75: //---------------- SU Subtract + this.procTime += 3; + this.D = this.bitFlip(this.D, 40); // complement the D sign + this.integerAdd(); + break; + + case 0x76: //---------------- ADA Add Absolute + this.procTime += 3; + this.D = this.bitReset(this.D, 40); // clear the D sign + this.integerAdd(); + break; + + case 0x77: //---------------- SUA Subtract Absolute + this.procTime += 3; + this.D = this.bitSet(this.D, 40); // set the D sign + this.integerAdd(); + break; + + // 0x78-0x79: //---------------- (no op) + + case 0x80: //---------------- FAD Floating Add + this.procTime += 4; + this.floatingAdd(); + break; + + case 0x81: //---------------- FSU Floating Subtract + this.procTime += 4; + // Complement the D sign -- any sign overflow will be ignored by floatingAdd. + this.D += 0x10000000000; + this.floatingAdd(); + break; + + case 0x82: //---------------- FM Floating Multiply + this.procTime += 3; + this.floatingMultiply(); + break; + + case 0x83: //---------------- FDIV Floating Divide + this.procTime += 3; + this.floatingDivide(); + break; + + // 0x84: //---------------- (no op) + + case 0x85: //---------------- MSH Memory Search High (Eaton CER) + this.searchMemory(true); + return; // avoid the executeComplete() + break; + + // 0x86: //---------------- (no op) + + case 0x87: //---------------- MSE Memory Search Equal (Eaton CER) + this.searchMemory(false); + return; // avoid the executeComplete() + break; + + // 0x88-0x89: //---------------- (no op) + + case 0x90: //---------------- FAA Floating Add Absolute + this.procTime += 4; + this.D %= 0x10000000000; // clear the D-sign digit + this.floatingAdd(); + break; + + case 0x91: //---------------- FSA Floating Subtract Absolute + this.procTime += 4; + this.D = this.D%0x10000000000 + 0x10000000000; // set the D-sign + this.floatingAdd(); + break; + + case 0x92: //---------------- FMA Floating Multiply Absolute + this.procTime += 3; + this.D %= 0x10000000000; // clear the D-sign digit + this.floatingMultiply(); + break; + + case 0x93: //---------------- FDA Floating Divide Absolute + this.procTime += 3; + this.D %= 0x10000000000; // clear the D-sign digit + this.floatingDivide(); + break; + + // 0x94-0x99: //---------------- (no op) + + } // switch this.COP + + this.executeComplete(); +}; + +/**************************************/ +B220Processor.prototype.execute = function execute() { + /* Implements the Execute cycle of the 205 processor. This is initiated either + by pressing START on one of the consoles with the Timing Toggle=0 (Execute), + or by the prior operation complete if the processor is in continuous mode */ + var d; // scratch digit + var w; // scratch word + var x; // scratch variable or counter + + w = this.C % 0x100000000; // C register operand and control addresses + this.CCONTROL = w % 0x10000; // C register control address + this.COP = (this.C - w)/0x100000000; // C register operation code + this.CADDR = (w - this.CCONTROL)/0x10000; // C register operand address + + this.togZCT = 0; // for display only + this.togT0 = 1; // for display only, leave it on for execute cycle + if (!this.sswLockNormal) { + this.setTimingToggle(1); // next cycle will be Fetch by default + } + + if ((this.COP & 0xF8) == 0x08) { // if STOP (08) operator or + this.togCST = 1; // halt the processor + this.stopControl = 1; // turn on CONTROL lamp + this.executeComplete(); + } else if (this.stopOverflow && (this.COP & 0x08) == 0) { // overflow and op is not a CC + this.togCST = 1; // halt the processor + this.stopControl = 1; // turn on CONTROL lamp + this.executeComplete(); + } else if (this.COP >= 0x60) { // if operator requires an operand + ++this.procTime; // minimum alpha for Class II is 5 word-times + this.clearControlToggles(); + this.readMemory(this.executeWithOperand); + } else { // otherwise execute a non-operand instruction + this.clearControlToggles(); + this.procTime += 3; // minimum Class I execution is 3 word-times + + switch (this.COP) { + case 0x00: //---------------- PTR Paper-tape/keyboard read + this.D = 0; + this.togSTART = 1; + this.consoleInputDigit(); + break; + + case 0x01: //---------------- CIRA Circulate A + x = B220Processor.bcdBinary(this.CADDR % 0x20); + this.procTime += x+8; + x = 19-x; + this.SHIFT = B220Processor.binaryBCD(x); // for display only + this.togDELAY = 1; // for display only + w = this.A; + for (; x<=19; ++x) { + d = (w - w%0x10000000000)/0x10000000000; + w = (w%0x10000000000)*0x10 + d; + } + this.A = w; + this.executeComplete(); + break; + + case 0x02: //---------------- STC Store and Clear A + this.procTime += 1; + this.writeMemory(this.executeComplete, true); + break; + + case 0x03: //---------------- PTW Paper-tape/Flexowriter write + if (this.cswPOSuppress) { + this.executeComplete(); // ignore printout commands + } else if (this.cswOutput == 0) { + this.togCST = 1; // halt if Output switch is OFF + this.executeComplete(); + } else { + this.togPO1 = 1; // for display only + this.SHIFT = this.bcdAdd(this.CADDR%0x20, 0x19, 1, 1); // 19-n + this.procTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O + this.stopIdle = 1; // turn IDLE lamp on in case Output Knob is OFF + d = (this.CADDR%0x1000 - this.CADDR%0x100)/0x100; + if (d) { // if C8 is not zero, output it first as the format digit + this.togOK = this.togDELAY = 1; // for display only + this.console.writeFormatDigit(d, this.boundConsoleOutputSignDigit); + } else { + this.consoleOutputSignDigit(); + } + } + break; + + case 0x04: //---------------- CNZ Change on Non-Zero + this.procTime += 3; + this.togZCT = 1; // for display only + this.D = 0; + this.integerAdd(); // clears the sign digit, among other things + if (this.A) { + this.setTimingToggle(0); // stay in Execute + this.setOverflow(1); // set overflow + this.COP = 0x28; // make into a CC + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + } + this.executeComplete(); + break; + + // 0x05: //---------------- (no op) + + case 0x06: //---------------- UA Unit Adjust + this.procTime += 2; + if (this.A % 2 == 0) { + ++this.A; + } + this.executeComplete(); + break; + + case 0x07: //---------------- PTWF Paper-tape Write Format + if (this.cswPOSuppress) { + this.executeComplete(); // ignore printout commands + } else if (this.cswOutput == 0) { + this.togCST = 1; // halt if Output switch is OFF + this.executeComplete(); + } else { + this.procTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O + this.stopIdle = 1; // turn IDLE lamp on in case Output Knob is OFF + d = (this.CADDR%0x1000 - this.CADDR%0x100)/0x100; + if (d) { // if C8 is not zero, output it as the format digit + this.togOK = this.togDELAY = 1; + this.console.writeFormatDigit(d, this.boundConsoleOutputFinished); + } else { + this.consoleOutputFinished(); + } + } + break; + + // 0x08: //---------------- HALT [was handled above] + + // 0x09: //---------------- (no op) + + case 0x10: //---------------- DAD Digit Add from keyboard to A + this.togSTART = 1; + this.procTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O + this.console.readDigit(this.boundConsoleReceiveSingleDigit); + break; + + case 0x11: //---------------- BA B to A transfer + this.procTime += 3; + this.togBTOAIN = 1; // for display only + this.togADDAB = 1; // for display only + this.togDELTABDIV = 1; // for display only + this.SHIFT = 0x15; // for display only + this.A = this.bcdAdd(0, this.B); + this.executeComplete(); + break; + + case 0x12: //---------------- ST Store A + this.procTime += 1; + this.writeMemory(this.executeComplete, false); + break; + + case 0x13: //---------------- SR Shift Right A & R + x = B220Processor.bcdBinary(this.CADDR % 0x20); + this.procTime += (x < 12 ? 2 : 3); + x = 19-x; + this.SHIFT = B220Processor.binaryBCD(x); // for display only + this.SHIFTCONTROL = 0x04; // for display only + w = this.A % 0x10000000000; // A sign is not affected + for (; x<19; ++x) { + d = w % 0x10; + w = (w-d)/0x10; + this.R = (this.R - this.R % 0x10)/0x10 + d*0x1000000000; + } + this.A += w - this.A%0x10000000000; // restore the sign + this.executeComplete(); + break; + + case 0x14: //---------------- SL Shift Left A & R + x = B220Processor.bcdBinary(this.CADDR % 0x20); + this.procTime += (x < 9 ? 3 : 2); + this.SHIFT = B220Processor.binaryBCD(x); // for display only + this.SHIFTCONTROL = 0x06; // for display only + w = this.A % 0x10000000000; // A sign is not affected + for (; x<=19; ++x) { + d = this.R % 0x10; + this.R = (this.R - d)/0x10; + d = (w += d*0x10000000000)%0x10; + w = (w-d)/0x10; + this.R += d*0x1000000000; + } + this.A += w - this.A%0x10000000000; // restore the sign + this.executeComplete(); + break; + + case 0x15: //---------------- NOR Normalize A & R + this.togZCT = 1; // for display only + this.SHIFTCONTROL = 0x02; // for display only + w = this.A % 0x10000000000; // A sign is not affected + x = 0; + do { + d = (w - w%0x1000000000)/0x1000000000; + if (d) { + break; // out of do loop + } else { + ++x; // count the shifts + d = this.R % 0x1000000000; // shift A and R left + w = w*0x10 + (this.R - d)/0x1000000000; + this.R = d*0x10; + } + } while (x < 10); + this.A += w - this.A%0x10000000000; // restore the sign + this.procTime += (x+1)*2; + + this.SPECIAL = x; // the result + this.SHIFTCONTROL |= 0x04; // for display only + this.SHIFT = 0x19; // for display only + if (x < 10) { + } else { + this.setTimingToggle(0); // stay in Execute + this.setOverflow(1); // set overflow + this.COP = 0x28; // make into a CC + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + } + this.executeComplete(); + break; + + case 0x16: //---------------- ADSC Add Special Counter to A + this.procTime += 3; + this.D = this.SPECIAL; + this.integerAdd(); + this.executeComplete(); + break; + + case 0x17: //---------------- SUSC Subtract Special Counter from A + this.procTime += 3; + this.D = this.SPECIAL + 0x10000000000; // set to negative + this.integerAdd(); + this.executeComplete(); + break; + + // 0x18-0x19: //---------------- (no op) + + case 0x20: //---------------- CU Change Unconditionally + this.procTime += 2; + this.SHIFT = 0x15; // for display only + this.SHIFTCONTROL = 0x07; // for display only + this.CCONTROL = this.CADDR; // copy address to control counter + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + this.executeComplete(); + break; + + case 0x21: //---------------- CUR Change Unconditionally, Record + this.procTime += 2; + this.SHIFT = 0x15; // for display only + this.SHIFTCONTROL = 0x07; // for display only + this.R = this.CCONTROL*0x1000000; // save current control counter + this.CCONTROL = this.CADDR; // copy address to control counter + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + this.executeComplete(); + break; + + case 0x22: //---------------- DB Decrease B and Change on Negative + this.procTime += 3; + this.togADDAB = 1; // for display only + this.togDELTABDIV = 1; // for display only + this.togZCT = 1; // for display only + this.SHIFT = 0x15; // for display only + if (this.B == 0) { + this.B = 0x9999; + } else { + this.B = this.bcdAdd(this.B, 0x9999) % 0x10000; // add -1 + this.setTimingToggle(0); // stay in Execute + this.setOverflow(1); // set overflow + this.COP = 0x28; // make into a CC + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + } + this.togSIGN = ((this.A - this.A%0x10000000000)/0x10000000000) & 0x01; // display only + this.D = 0; + this.executeComplete(); + break; + + case 0x23: //---------------- RO Round A, Clear R + this.procTime += 3; + this.togSIGN = ((this.A - this.A%0x10000000000)/0x10000000000) & 0x01; + // Add round-off (as the carry bit) to absolute value of A. + this.A = this.bcdAdd(this.A%0x10000000000, 0, 0, (this.R < 0x5000000000 ? 0 : 1)); + if (this.A >= 0x10000000000) { + this.setOverflow(1); // overflow occurred + this.A -= 0x10000000000; // remove the overflow bit + } + this.A += this.togSIGN*0x10000000000; // restore the sign bit in A + this.D = this.R = 0; // clear D & R + this.executeComplete(); + break; + + case 0x24: //---------------- BF4 Block from 4000 Loop + case 0x25: //---------------- BF5 Block from 5000 loop + case 0x26: //---------------- BF6 Block from 6000 loop + case 0x27: //---------------- BF7 Block from 7000 loop + this.procTime +=2; + this.blockFromLoop(this.COP-0x20, this.executeComplete); + break; + + case 0x28: //---------------- CC Change Conditionally + if (!this.stopOverflow) { // check if branch should occur + this.procTime += 1; + this.SHIFTCONTROL = 0x04; // no -- set for display only + } else { + this.procTime += 2; + this.setOverflow(0); // reset overflow and do the branch + this.SHIFT = 0x15; // for display only + this.SHIFTCONTROL = 0x07; // for display only + this.CCONTROL = this.CADDR; // copy address to control counter + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + } + this.executeComplete(); + break; + + case 0x29: //---------------- CCR Change Conditionally, Record + if (!this.stopOverflow) { // check if branch should occur + this.procTime += 1; + this.SHIFTCONTROL = 0x04; // for display only + } else { + this.procTime += 2; + this.setOverflow(0); // reset overflow and do the branch + this.SHIFT = 0x15; // for display only + this.SHIFTCONTROL = 0x07; // for display only + this.R = this.CCONTROL*0x1000000; // save current control counter + this.CCONTROL = this.CADDR; // copy address to control counter + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + } + this.executeComplete(); + break; + + case 0x30: //---------------- CUB Change Unconditionally, Block to 7000 Loop + this.procTime += 4; + this.SHIFT = 0.15; // for display only + this.SHIFTCONTROL = 0x0F; // for display only + this.CCONTROL = this.CADDR%0x100 + 0x7000; // set control to loop-7 address + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + this.blockToLoop(7, this.executeComplete); + break; + + case 0x31: //---------------- CUBR Change Unconditionally, Block to 7000 Loop, Record + this.procTime += 4; + this.SHIFT = 0.15; // for display only + this.SHIFTCONTROL = 0x0F; // for display only + this.R = this.CCONTROL*0x1000000; // save current control counter + this.CCONTROL = this.CADDR%0x100 + 0x7000; // set control to loop-7 address + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + this.blockToLoop(7, this.executeComplete); + break; + + case 0x32: //---------------- IB Increase B + this.procTime += 3; + this.togADDAB = 1; // for display only + this.togDELTABDIV = 1; // for display only + this.togZCT = 1; // for display only + this.SHIFT = 0x15; // for display only + this.B = this.bcdAdd(this.B, 1) % 0x10000; // discard any overflow in B + this.togSIGN = ((this.A - this.A%0x10000000000)/0x10000000000) & 0x01; // display only + this.D = 0; + this.executeComplete(); + break; + + case 0x33: //---------------- CR Clear R + this.procTime += 2; + this.SHIFTCONTROL = 0x04; // for display only + this.R = 0; + this.executeComplete(); + break; + + case 0x34: //---------------- BT4 Block to 4000 Loop + case 0x35: //---------------- BT5 Block to 5000 Loop + case 0x36: //---------------- BT6 Block to 6000 Loop + case 0x37: //---------------- BT7 Block to 7000 Loop + this.procTime +=2; + this.blockToLoop(this.COP-0x30, this.executeComplete); + break; + + case 0x38: //---------------- CCB Change Conditionally, Block to 7000 Loop + if (!this.stopOverflow) { // check if branch should occur + this.procTime += 3; + this.SHIFTCONTROL = 0x04; // for display only + this.executeComplete(); + } else { + this.procTime += 4; + this.setOverflow(0); // reset overflow and do the branch + this.SHIFT = 0x15; // for display only + this.SHIFTCONTROL = 0x0F; // for display only + this.CCONTROL = this.CADDR%0x100 + 0x7000; // set control to loop-7 address + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + this.blockToLoop(7, this.executeComplete); + } + break; + + case 0x39: //---------------- CCBR Change Conditionally, Block to 7000 Loop, Record + if (!this.stopOverflow) { // check if branch should occur + this.procTime += 3; + this.SHIFTCONTROL = 0x04; // for display only + this.executeComplete(); + } else { + this.procTime += 4; + this.setOverflow(0); // reset overflow and do the branch + this.SHIFT = 0x15; // for display only + this.SHIFTCONTROL = 0x0F; // for display only + this.R = this.CCONTROL*0x1000000; // save current control counter + this.CCONTROL = this.CADDR%0x100 + 0x7000; // set control to loop-7 address + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + this.blockToLoop(7, this.executeComplete); + } + break; + + case 0x40: //---------------- MTR Magnetic Tape Read + if (!this.magTape) { + this.executeComplete(); + } else { + this.selectedUnit = (this.CEXTRA >>> 4) & 0x0F; + d = (this.CEXTRA >>> 8) & 0xFF; // number of blocks + this.togMT3P = 1; + this.togMT1BV4 = d & 0x01; // select initial loop buffer + this.togMT1BV5 = 1-this.togMT1BV4; + this.procTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O + if (this.magTape.read(this.selectedUnit, d, this.boundMagTapeReceiveBlock)) { + this.setOverflow(1); // control or tape unit busy/not-ready + this.togMT3P = this.togMT1BV4 = this.togMT1BV5 = 0; + this.executeComplete(); + } + } + break; + + // 0x41: //---------------- (no op) + + case 0x42: //---------------- MTS Magnetic Tape Search + if (this.magTape) { + this.selectedUnit = (this.CEXTRA >>> 4) & 0x0F; + d = (this.CEXTRA >>> 8) & 0xFF; // lane number + if (this.magTape.search(this.selectedUnit, d, this.CADDR)) { + this.setOverflow(1); // control or tape unit busy/not-ready + } + } + this.executeComplete(); + break; + + // 0x43: //---------------- (no op) + + case 0x44: //---------------- CDR Card Read (Cardatron) + this.D = 0; + if (!this.cardatron) { + this.executeComplete(); + } 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.procTime -= 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 = this.CCONTROL*0x1000000; + this.setTimingToggle(0); // stay in Execute + this.setOverflow(1); // set overflow + this.COP = 0x28; // make into a CC + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + } + this.executeComplete(); + break; + + // 0x46-0x47: //---------------- (no op) + + case 0x48: //---------------- CDRF Card Read Format + if (!this.cardatron) { + this.executeComplete(); + } 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.procTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O + this.cardatron.inputFormatInitiate(this.selectedUnit, this.kDigit, + this.boundCardatronOutputWord, this.boundCardatronOutputFinished); + } + break; + + // 0x49: //---------------- (no op) + + case 0x50: //---------------- MTW Magnetic Tape Write + if (!this.magTape) { + this.executeComplete(); + } else { + this.selectedUnit = (this.CEXTRA >>> 4) & 0x0F; + d = (this.CEXTRA >>> 8) & 0xFF; // number of blocks + this.togMT3P = 1; + this.togMT1BV4 = d & 0x01; // select initial loop buffer + this.togMT1BV5 = 1-this.togMT1BV4; + this.procTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O + if (this.magTape.write(this.selectedUnit, d, this.boundMagTapeInitiateSend)) { + this.setOverflow(1); // control or tape unit busy/not-ready + this.togMT3P = this.togMT1BV4 = this.togMT1BV5 = 0; + this.executeComplete(); + } + } + break; + + // 0x51: //---------------- (no op) + + case 0x52: //---------------- MTRW Magnetic Tape Rewind + if (this.magTape) { + this.selectedUnit = (this.CEXTRA >>> 4) & 0x0F; + if (this.magTape.rewind(this.selectedUnit)) { + this.setOverflow(1); // control or tape unit busy/not-ready + } + } + this.executeComplete(); + break; + + // 0x53: //---------------- (no op) + + case 0x54: //---------------- CDW Card Write (Cardatron) + if (!this.cardatron) { + this.executeComplete(); + } 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.procTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O + this.cardatron.outputInitiate(this.selectedUnit, this.kDigit, (this.CEXTRA >>> 12) & 0x0F, + this.boundCardatronOutputWord, this.boundCardatronOutputFinished); + } + break; + + case 0x55: //---------------- CDWI Card Write Interrogate + this.selectedUnit = (this.CEXTRA >>> 4) & 0x07; + if (this.cardatron && this.cardatron.outputReadyInterrogate(this.selectedUnit)) { + this.R = this.CCONTROL*0x1000000; + this.setTimingToggle(0); // stay in Execute + this.setOverflow(1); // set overflow + this.COP = 0x28; // make into a CC + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + } + this.executeComplete(); + break; + + // 0x56-0x57: //---------------- (no op) + + case 0x58: //---------------- CDWF Card Write Format + if (!this.cardatron) { + this.executeComplete(); + } 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.procTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O + this.cardatron.outputFormatInitiate(this.selectedUnit, this.kDigit, + this.boundCardatronOutputWord, this.boundCardatronOutputFinished); + } + break; + + // 0x59: //---------------- (no op) + + default: //---------------- (unimplemented instruction -- no op) + this.executeComplete(); + break; + } // switch this.COP + } +}; + +/**************************************/ +B220Processor.prototype.start = function start() { + /* Initiates the processor according to the current Timing Toggle state */ + var drumTime = performance.now()*B220Processor.wordsPerMilli; + + if (this.togCST && this.poweredOn) { + this.procStart = drumTime; + this.procTime = drumTime; + this.memoryStopTime = drumTime; + + this.stopIdle = 0; + this.stopControl = 0; + this.stopBreakpoint = this.togBKPT = 0; + // stopOverflow, stopSector, and stopForbidden are not reset by the START button + + this.togCST = 0; + if (this.togTiming && !this.sswLockNormal) { + this.fetch(); + } else { + this.setTimingToggle(0); + this.execute(); + } + } +}; + +/**************************************/ +B220Processor.prototype.stop = function stop() { + /* Stops running the processor on the Javascript thread */ + + if (this.poweredOn) { + this.togCST = 1; + this.cctContinuous = 0; + this.stopIdle = 1; // turn on the IDLE lamp + if (this.scheduler) { + clearCallback(this.scheduler); + this.scheduler = 0; + } + } +}; + +/**************************************/ +B220Processor.prototype.inputSetup = function inputSetup(unitNr) { + /* Called from the Cardatron Control Unit. If the Processor is stopped, + loads a CDR (44) instruction into C for unit "unitNr" */ + + if (this.togCST && this.poweredOn) { + this.CEXTRA = unitNr*0x10; + this.COP = 0x44; + this.CADDR = 0; + this.C = (this.COP*0x10000 + this.CADDR)*0x10000 + this.CCONTROL; + this.setTimingToggle(0); + } +}; + +/**************************************/ +B220Processor.prototype.powerUp = function powerUp() { + /* Powers up the system */ + + if (!this.poweredOn) { + this.clear(); + this.poweredOn = 1; + this.console = this.devices.ControlConsole; + this.cardatron = this.devices.CardatronControl; + this.magTape = this.devices.MagTapeControl; + this.lastGlowTime = performance.now()*B220Processor.wordsPerMilli; + this.glowTimer = setInterval(this.boundUpdateLampGlow, B220Processor.lampGlowInterval); + } +}; + +/**************************************/ +B220Processor.prototype.powerDown = function powerDown() { + /* Powers down the system */ + + if (this.poweredOn) { + this.stop(); + this.clear(); + this.poweredOn = 0; + this.cardatron = null; + this.console = null; + this.magTape = null; + if (this.glowTimer) { + clearInterval(this.glowTimer); + this.glowTimer = null; + } + } +}; + +/**************************************/ +B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() { + /* Loads a set of default demo programs to the memory drum */ + + // Simple counter speed test + this.MM[ 0] = 0x0000740002; // ADD 2 + this.MM[ 1] = 0x0000200000; // CU 0 + this.MM[ 2] = 0x0000000001; // LIT 1 + + // Bootstrap to loop-based square-roots test + 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" + + // Tom Sawyer's "Square Roots 100" (Babylonian or Newton's method): + this.MM[ 100] = 0x640139; // CAD 139 + this.MM[ 101] = 0x120138; // ST 138 + this.MM[ 102] = 0x640139; // CAD 139 + this.MM[ 103] = 0x130005; // SR 5 + this.MM[ 104] = 0x610138; // DIV 138 + this.MM[ 105] = 0x120137; // ST 137 + this.MM[ 106] = 0x750138; // SUB 138 + this.MM[ 107] = 0x120136; // ST 136 + this.MM[ 108] = 0x660136; // CADA 136 + this.MM[ 109] = 0x750135; // SUB 135 + this.MM[ 110] = 0x730135; // OSGD 135 + this.MM[ 111] = 0x280118; // CC 118 + this.MM[ 112] = 0x640138; // CAD 138 + this.MM[ 113] = 0x740137; // ADD 137 + this.MM[ 114] = 0x130005; // SR 5 + this.MM[ 115] = 0x610134; // DIV 134 + this.MM[ 116] = 0x120138; // ST 138 + this.MM[ 117] = 0x200102; // CU 102 + this.MM[ 118] = 0x640139; // CAD 139 + this.MM[ 119] = 0x030505; // PTW 0505 + this.MM[ 120] = 0x640137; // CAD 137 + this.MM[ 121] = 0x030810; // PTW 0810 + this.MM[ 122] = 0x640139; // CAD 139 + this.MM[ 123] = 0x740133; // ADD 133 + this.MM[ 124] = 0x120139; // ST 139 + this.MM[ 125] = 0x200102; // CU 102 + this.MM[ 126] = 0; + this.MM[ 127] = 0; + this.MM[ 128] = 0; + this.MM[ 129] = 0; + this.MM[ 130] = 0; + this.MM[ 131] = 0; + this.MM[ 132] = 0; + this.MM[ 133] = 0x100000; + this.MM[ 134] = 0x200000; + this.MM[ 135] = 0x10; + this.MM[ 136] = 0; + this.MM[ 137] = 0; + this.MM[ 138] = 0; + this.MM[ 139] = 0x200000; + + // "Square Roots 100" running from the loops with R cleared for division: + // Block for the 6980 loop + this.MM[ 200] = 0x0000647039; // CAD 7039 + this.MM[ 201] = 0x0000127038; // ST 7038 + this.MM[ 202] = 0x0000647039; // CAD 7039 + this.MM[ 203] = 0x0000330000; // CR + this.MM[ 204] = 0x0000130005; // SR 5 + this.MM[ 205] = 0x0000617038; // DIV 7038 + this.MM[ 206] = 0x0000127037; // ST 7037 + this.MM[ 207] = 0x0000757038; // SUB 7038 + this.MM[ 208] = 0x0000127036; // ST 7036 + this.MM[ 209] = 0x0000667036; // CADA 7036 + this.MM[ 210] = 0x0000757035; // SUB 7035 + this.MM[ 211] = 0x0000737035; // OSGD 7035 + this.MM[ 212] = 0x0000287000; // CC 7000 + this.MM[ 213] = 0x0000647038; // CAD 7038 + this.MM[ 214] = 0x0000747037; // ADD 7037 + this.MM[ 215] = 0x0000330000; // CR + this.MM[ 216] = 0x0000130005; // SR 5 + this.MM[ 217] = 0x0000617034; // DIV 7034 + this.MM[ 218] = 0x0000127038; // ST 7038 + this.MM[ 219] = 0x0000206982; // CU 6982 + // Block for the 7000 loop + this.MM[ 220] = 0x0000647039; // CAD 7039 + this.MM[ 221] = 0x0000030505; // PTW 0505 + this.MM[ 222] = 0x0000647037; // CAD 7037 + this.MM[ 223] = 0x0000030810; // PTW 0810 + this.MM[ 224] = 0x0000647039; // CAD 7039 + this.MM[ 225] = 0x0000747033; // ADD 7033 + this.MM[ 226] = 0x0000127039; // ST 7039 + this.MM[ 227] = 0x0000206982; // CU 6982 + this.MM[ 228] = 0; + this.MM[ 229] = 0; + this.MM[ 230] = 0; + this.MM[ 231] = 0; + this.MM[ 232] = 0; + this.MM[ 233] = 0x0000100000; + this.MM[ 234] = 0x0000200000; + this.MM[ 235] = 0x0000000010; + this.MM[ 236] = 0; + this.MM[ 237] = 0; + this.MM[ 238] = 0; + this.MM[ 239] = 0x0000200000; + + // "Square Roots 100" adapted for floating-point and relative precision: + this.MM[ 300] = 0x0000640339; // CAD 339 load initial argument + this.MM[ 301] = 0x0000120338; // ST 338 store as initial upper bound + this.MM[ 302] = 0x0000640339; // CAD 339 start of loop: load current argument + this.MM[ 303] = 0x0000330000; // CR clear R + this.MM[ 304] = 0x0000830338; // FDIV 338 divide argument by upper bound + this.MM[ 305] = 0x0000120337; // ST 337 store as current result + this.MM[ 306] = 0x0000830338; // FDIV 338 ratio to upper bound + this.MM[ 307] = 0x0000120336; // ST 336 store as current precision + this.MM[ 308] = 0x0000660335; // CADA 335 load target precision + this.MM[ 309] = 0x0000810336; // FSU 336 subtract current precision + this.MM[ 310] = 0x0000730335; // OSGD 335 if current precision > target precision + this.MM[ 311] = 0x0000280318; // CC 318 we're done -- jump out to print + this.MM[ 312] = 0x0000640338; // CAD 338 load current upper bound + this.MM[ 313] = 0x0000800337; // FAD 337 add current result + this.MM[ 314] = 0x0000330000; // CR clear R + this.MM[ 315] = 0x0000830334; // FDIV 334 divide by 2.0 to get new upper bound + this.MM[ 316] = 0x0000120338; // ST 338 store new upper bound + this.MM[ 317] = 0x0000200302; // CU 302 do another iteration + this.MM[ 318] = 0x0001640339; // CAD 339 + this.MM[ 319] = 0x0000030510; // PTW 0510 + this.MM[ 320] = 0x0000640337; // CAD 337 + this.MM[ 321] = 0x0000030810; // PTW 0810 + this.MM[ 322] = 0x0000640339; // CAD 339 load argument value + this.MM[ 323] = 0x0000800333; // FAD 333 add 1 to argument value + this.MM[ 324] = 0x0000120339; // ST 339 + this.MM[ 325] = 0x0000200301; // CU 301 start sqrt for next argument value + this.MM[ 326] = 0; + this.MM[ 327] = 0; + this.MM[ 328] = 0; + this.MM[ 329] = 0; + this.MM[ 330] = 0; + this.MM[ 331] = 0; + this.MM[ 332] = 0; + this.MM[ 333] = 0x05110000000; // 1.0 literal: argument increment + this.MM[ 334] = 0x05120000000; // 2.0 literal + this.MM[ 335] = 0x05099999990; // 0.99999990 literal: target precision + this.MM[ 336] = 0; // current precision + this.MM[ 337] = 0; // current sqrt result + this.MM[ 338] = 0; // current upper bound on result + this.MM[ 339] = 0x05120000000; // 2.0 sqrt argument + + // Counter speed test in 4000 loop + this.L4[ 0] = 0x0000744002; // ADD 4002 -- start of counter speed test + this.L4[ 1] = 0x0000204000; // CU 4000 + this.L4[ 2] = 0x0000000001; // LIT 1 +}; \ No newline at end of file diff --git a/index.html b/index.html index 01ef268..91fe71c 100644 --- a/index.html +++ b/index.html @@ -31,11 +31,11 @@

Main Links