1
0
mirror of https://github.com/pkimpel/retro-220.git synced 2026-01-22 10:31:05 +00:00
pkimpel.retro-220/emulator/B220Processor.js

3422 lines
154 KiB
JavaScript
Raw Blame History

/***********************************************************************
* 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 <20>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<loop.length; ++x) {
this.D = w = block[x]; // D for display only
if (w < 0x20000000000) {
this.togCLEAR = 1; // no B modification
} else {
// Adjust sign digit and do B modification as necessary
sign = ((w - w%0x10000000000)/0x10000000000) % 0x08; // low-order 3 bits only
if (this.tswSuppressB) {
this.togCLEAR = 1; // no B modification
} else {
this.togCLEAR = ((sign & 0x02) ? 0 : 1);
sign &= 0x01;
}
w = sign*0x10000000000 + w%0x10000000000;
}
if (this.togCLEAR) {
w = this.bcdAdd(w, 0);
} else {
w = this.bcdAdd(w, this.B);
}
loop[x] = w;
} // for x
this.A = w; // for display only
// Block the loop buffer to main memory if appropriate
if (this.CADDR < 0x8000) {
this.blockFromLoop((this.togMT1BV4 ? 4 : 5), blockStoreComplete);
} else {
blockStoreComplete();
}
}
}
return aborted;
};
/***********************************************************************
* External Control Module *
***********************************************************************/
/**************************************/
B220Processor.prototype.setExternalSwitches = function setExternalSwitches() {
/* Sets the eight external switches from the most-significant digits of
the D register */
var d; // current D-register digit
var w = this.D % 0x10000000000; // working copy of D word
var x; // digit index
for (x=7; 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
};