mirror of
https://github.com/pkimpel/retro-220.git
synced 2026-01-11 23:52:46 +00:00
1. Apply corrections to emulator/B220Processor.js uncovered by the
paper-tape diagnostic routines acquired by Al Kossow:
a. Integer divide: sign of R must be preserved on overflow,
otherwise must be set to sign of the dividend (A register).
b. Floating division: sign of R must be preserved, correct align-
ment of operands in registers before starting division cycles,
perform 11 division cycles instead of 10, reconstruct A and R
registers if exponent overflow is detected during post-divide
mantissa normalization.
2. Disable console keyboard after ADD button is pressed.
3. Correct alphanumeric character code translation used by the internal
memory dump routine to match that for console and paper-tape I/O.
4. Fix bug with backward magnetic tape positioning (MPB) for tape
blocks less than 20 words long.
1183 lines
49 KiB
JavaScript
1183 lines
49 KiB
JavaScript
/***********************************************************************
|
|
* retro-220/webUI B220MagTapeControl.js
|
|
************************************************************************
|
|
* Copyright (c) 2017, Paul Kimpel.
|
|
* Licensed under the MIT License, see
|
|
* http://www.opensource.org/licenses/mit-license.php
|
|
************************************************************************
|
|
* Burroughs 220 MagTape Control unit module.
|
|
************************************************************************
|
|
* 2017-07-09 P.Kimpel
|
|
* Original version, from retro-205 D205MagTapeControl.js.
|
|
***********************************************************************/
|
|
"use strict";
|
|
|
|
/**************************************/
|
|
function B220MagTapeControl(p) {
|
|
/* Constructor for the MagTapeControl object */
|
|
var left = 0; // control window left position
|
|
var top = 452; // control window top-from-bottom position
|
|
var u; // unit configuration object
|
|
var x; // unit index
|
|
|
|
this.config = p.config; // System configuration object
|
|
this.mnemonic = "MCU";
|
|
this.p = p; // B220Processor object
|
|
this.releaseProcessor = p.boundMagTapeComplete;
|
|
|
|
// Do not call this.clear() here -- call this.clearUnit() from onLoad instead
|
|
|
|
this.doc = null;
|
|
this.window = null;
|
|
B220Util.openPopup(window, "../webUI/B220MagTapeControl.html", this.mnemonic,
|
|
"location=no,scrollbars=no,resizable,width=712,height=144,top=" +
|
|
(screen.availHeight - top) + ",left=" + left,
|
|
this, B220MagTapeControl.prototype.magTapeOnLoad);
|
|
|
|
this.boundReleaseControl = B220MagTapeControl.prototype.releaseControl.bind(this);
|
|
this.boundCancelIO = B220MagTapeControl.prototype.cancelIO.bind(this);
|
|
this.boundTapeUnitFinished = B220MagTapeControl.prototype.tapeUnitFinished.bind(this);
|
|
this.boundFetchWord = B220MagTapeControl.prototype.fetchWord.bind(this);
|
|
this.boundStoreWord = B220MagTapeControl.prototype.storeWord.bind(this);
|
|
this.boundSwitch_Click = B220MagTapeControl.prototype.switch_Click.bind(this);
|
|
|
|
this.currentUnit = null; // stashed tape unit object
|
|
|
|
/* driveState is a status object passed to mag tape units that allows them
|
|
to report their status back to the control unit */
|
|
|
|
this.driveState = {
|
|
state: 0, // state/error code, see below
|
|
preface: 0, // preface/block length word
|
|
keyword: 0, // block keyword (first data word)
|
|
controlWord: 0, // block controlword (last data word)
|
|
startTime: 0, // start time for the operation (ms)
|
|
completionDelay: 0, // extra delay before drive is released (ms)
|
|
// State constants
|
|
driveNoError: 0, // operation successful
|
|
driveNotReady: 1, // drive not ready or rewind-lockout
|
|
driveBusy: 2, // drive busy
|
|
driveAtBOT: 3, // tape at physical BOT
|
|
driveAtEOT: 4, // tape at physical EOT
|
|
driveAtEOI: 5, // tape at end-of-information
|
|
driveHasControlWord: 6, // drive returned an EOT- or control-block control word
|
|
drivePrefaceCheck: 10, // invalid preface word
|
|
drivePrefaceMismatch: 12, // preface/instruction block-length mismatch
|
|
driveReadCheck: 13, // preface/tape block-length mismatch
|
|
driveInvalidBlockLength: 14, // invalid block length from instruction
|
|
driveMemoryError: 15, // memory address or parity error
|
|
driveNotEditedTape: 16, // attempt to initial-write over non-edited tape
|
|
driveUndefined: 99}; // undefined error
|
|
|
|
/* Set up the tape units from the system configuration. These can be any
|
|
combination of Tape Storage Units (DataReaders) and DataFiles. The indexes
|
|
into this array are physical unit numbers used internally -- unit designate
|
|
is set on the tape unit itself */
|
|
|
|
this.tapeUnit = [
|
|
null, // 0=not used
|
|
null, // tape unit A
|
|
null, // tape unit B
|
|
null, // tape unit C
|
|
null, // tape unit D
|
|
null, // tape unit E
|
|
null, // tape unit F
|
|
null, // tape unit G
|
|
null, // tape unit H
|
|
null, // tape unit I
|
|
null]; // tape unit J
|
|
|
|
for (x=1; x<this.tapeUnit.length; ++x) {
|
|
u = this.config.getNode("MagTape.units", x);
|
|
switch (u.type.substring(0, 2)) {
|
|
case "MT":
|
|
this.tapeUnit[x] = new B220MagTapeDrive(u.type, x, this, this.config);
|
|
break;
|
|
case "DF":
|
|
this.tapeUnit[x] = new B220DataFile(u.type, x, this, this.config);
|
|
break;
|
|
default:
|
|
this.tapeUnit[x] = null;
|
|
break;
|
|
} // switch u.type
|
|
} // for x
|
|
}
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.$$ = function $$(e) {
|
|
return this.doc.getElementById(e);
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.clear = function clear() {
|
|
/* Initializes (and if necessary, creates) the panel state */
|
|
|
|
this.C = 0; // C register (unit, block count, etc.)
|
|
this.T = 0; // T register
|
|
|
|
this.unitNr = 0; // current unit number from command
|
|
this.unitIndex = 0; // current index into this.tapeUnit[]
|
|
this.sField = 0; // starting digit position for field compare
|
|
this.LField = 0; // length of field compare
|
|
|
|
this.controlBusy = false; // control unit is busy with read/write/search
|
|
this.pendingCallee = null; // method to call for a pending operation
|
|
this.pendingArgs = null; // Arguments object for a pending operation
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.clearMisc = function clearMisc() {
|
|
/* Resets this.regMisc and the individual lamps for that register */
|
|
var bitNr;
|
|
var m = this.regMisc;
|
|
|
|
m.update(0);
|
|
for (bitNr=m.bits-1; bitNr>= 0; --bitNr) {
|
|
m.lamps[bitNr].set(0);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.fetchWord = function fetchWord(initialFetch) {
|
|
/* Returns the next word from the Processor's memory, as addressed by the low
|
|
four digits of its C register */
|
|
var word = this.p.boundMagTapeSendWord(initialFetch);
|
|
|
|
if (this.p.digitCheckAlarm.value) {
|
|
this.p.setMagneticTapeCheck(1);
|
|
return -1;
|
|
} else {
|
|
return word;
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.storeWord = function storeWord(initialStore, word) {
|
|
/* Stores the word value in the next word of the Processor's memory, as
|
|
addressed by the low four digits of its C register */
|
|
|
|
this.p.boundMagTapeReceiveWord(initialStore, word);
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.reportStatus = function reportStatus(state) {
|
|
/* Sets bits in the MISC register to indicate various drive and control unit
|
|
status and error conditions */
|
|
|
|
switch (state) {
|
|
case this.driveState.driveNoError:
|
|
this.clearMisc();
|
|
break;
|
|
case this.driveState.driveNotReady:
|
|
this.TX2Lamp.set(1);
|
|
this.TX10Lamp.set(1);
|
|
break;
|
|
case this.driveState.drivePrefaceCheck:
|
|
this.p.setMagneticTapeCheck(1);
|
|
this.TPCLamp.set(1);
|
|
break;
|
|
case this.driveState.drivePrefaceMismatch:
|
|
this.p.setMagneticTapeCheck(1);
|
|
this.TCFLamp.set(1);
|
|
this.C = (this.C & 0x00FFFF) | 0xFF0000;
|
|
this.regC.update(this.C);
|
|
break;
|
|
case this.driveState.driveReadCheck:
|
|
this.p.setMagneticTapeCheck(1);
|
|
this.TYC1Lamp.set(1);
|
|
this.TYC2Lamp.set(1);
|
|
this.C = (this.C & 0xFFF00F) | 0x000F90;
|
|
this.regC.update(this.C);
|
|
break;
|
|
case this.driveState.driveInvalidBlockLength:
|
|
this.p.setMagneticTapeCheck(1);
|
|
this.TX2Lamp.set(1);
|
|
this.TX4Lamp.set(1);
|
|
this.C = (this.C & 0x000F0F) | 0xB010F0;
|
|
this.regC.update(this.C);
|
|
break;
|
|
case this.driveState.driveNotEditedTape:
|
|
this.p.setMagneticTapeCheck(1);
|
|
break;
|
|
} // switch code
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.findDesignate = function findDesignate(u) {
|
|
/* Searches this.tapeUnit[] to find the internal index of the unit that is
|
|
designated as "u". If found, returns the internal index; if not found,
|
|
returns -1. If more than one ready unit with the same designate is found,
|
|
returns -2 */
|
|
var index = -1;
|
|
var unit;
|
|
var x;
|
|
|
|
for (x=this.tapeUnit.length-1; x>0; --x) {
|
|
unit = this.tapeUnit[x];
|
|
if (unit && unit.ready) {
|
|
if (unit.unitDesignate == u) {
|
|
if (index == -1) {
|
|
index = x;
|
|
} else {
|
|
index = -2;
|
|
break; // out of for loop
|
|
}
|
|
}
|
|
}
|
|
} // for x
|
|
|
|
return index;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.decrementBlockCount = function decrementBlockCount() {
|
|
/* Decrements the block-count digit in the C register. Returns true if the
|
|
remaining block count is non-zero, false otherwise */
|
|
var c1 = this.C; // will hold two high-order digits of C: uu0000
|
|
var c2 = c1%0x10000; // four low-order digits of C: bddd, b=block count
|
|
var result = true;
|
|
|
|
c1 -= c2;
|
|
if (c2 < 0x1000) {
|
|
c2 += 0x9000; // decrement 0x0ddd to 0x9ddd as count=0 => 10
|
|
} else {
|
|
c2 -= 0x1000; // decrement 0xddd..0x9ddd
|
|
if (c2 < 0x1000) {
|
|
result = false; // decremented 0x1ddd to 0x0ddd: no more blocks
|
|
}
|
|
}
|
|
|
|
this.C = c1+c2;
|
|
this.regC.update(this.C);
|
|
return result;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.determineBlockLength = function determineBlockLength(record) {
|
|
/* Determines the length of the next block to be read or written. If
|
|
"record" is true, the length is fetched from the next word in the Processor's
|
|
memory at the address in the Processor's C register. The length is converted
|
|
to binary and checked for valid values. Returns a negative value on error.
|
|
Note that the TCU always checks the kk digits from the instruction, even if
|
|
this is a record-mode operation and those digits do not determine block length */
|
|
var words = (this.C%0x1000 - this.C%0x10)/0x10; // kk digits from TCU C register
|
|
|
|
if (words > 0) {
|
|
words = (words >>> 4)*10 + words%0x10;
|
|
} else {
|
|
words = 100; // kk == 0 => 100
|
|
}
|
|
|
|
if (words < this.currentUnit.minBlockWords && words > 1) {
|
|
words = -1; // invalid kk digits in instruction
|
|
this.driveState.state = this.driveState.driveInvalidBlockLength;
|
|
} else if (record) {
|
|
words = this.fetchWord(true);
|
|
if (words < 0) { // memory fetch failed
|
|
this.driveState.state = this.driveState.driveMemoryError;
|
|
} else { // convert preface word to binary
|
|
words = ((words - words%0x100000000)/0x100000000)%0x100;
|
|
if (words > 0) {
|
|
words = (words >>> 4)*10 + words%0x10;
|
|
} else {
|
|
words = 100; // preface == 0 => 100
|
|
}
|
|
|
|
if (words < this.currentUnit.minBlockWords && words > 1) {
|
|
words = -1; // invalid preface read from memory
|
|
this.driveState.state = this.driveState.driveInvalidBlockLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
return words;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.determineFieldCompare = function determineFieldCompare(bReg) {
|
|
/* Determines the field to be compared during search and scan operations.
|
|
Decodes the sL value in bReg to this.sField and this.LField, checks that
|
|
s<L, and if not, sets the Processor's Program Check alarm. Returns true if
|
|
sL is invalid */
|
|
var result = false; // return value
|
|
|
|
this.sField = (bReg%0x10000 - bReg%0x1000)/0x1000;
|
|
if (this.sField == 0) {
|
|
this.sField = 10;
|
|
}
|
|
|
|
this.LField = (bReg%0x1000 - bReg%0x100)/0x100;
|
|
if (this.LField == 0) {
|
|
this.LField = 10;
|
|
}
|
|
|
|
if (this.sField < this.LField) {
|
|
result = true;
|
|
this.p.setProgramCheck(1);
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.compareKeywordField = function compareKeywordField(keyword) {
|
|
/* Compares "keyword" to the contents of the T register based on the partial-
|
|
word parameters this.sField and this.LField. Returns -1 if the keyword is
|
|
less than T, 0 if they are equal, and +1 if the keyword is greater than T */
|
|
var L = this.LField; // working copy of the field length
|
|
var adder = 0; // digit adder
|
|
var carry = 1; // decimal carry flag: 1 since we're subtracting
|
|
var equal = true; // equality flag: assume true initially
|
|
var kd = 0; // current keyword digit
|
|
var kw = keyword; // working copy of the keyword value
|
|
var s = this.sField; // working copy of starting digit number
|
|
var td = 0; // current T register digit
|
|
var tw = this.T; // working copy of the T register value
|
|
|
|
while (L > 0) {
|
|
kd = kw%0x10;
|
|
td = tw%0x10;
|
|
if (s < 10) {
|
|
++s; // just shift until s=10
|
|
} else {
|
|
--L;
|
|
adder = 9 - kd + td + carry;
|
|
if (adder < 10) {
|
|
carry = 0;
|
|
} else {
|
|
carry = 1;
|
|
adder -= 10
|
|
}
|
|
|
|
if (adder) {
|
|
equal = false;
|
|
}
|
|
}
|
|
|
|
kw = (kw-kd)/0x10; // shift both words right one digit
|
|
tw = (tw-td)/0x10;
|
|
} // while L
|
|
|
|
if (equal) {
|
|
return 0; // keyword equal T
|
|
} else if (carry) {
|
|
return -1; // keyword less than T
|
|
} else {
|
|
return 1; // keyword greater than T
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.queuePendingOperation = function queuePendingOperation(callee, args) {
|
|
/* Queues a pending tape operation */
|
|
|
|
//console.log(this.mnemonic + " queuePendingOperation: " + args[0].toString(16));
|
|
if (this.pendingCallee !== null) {
|
|
throw new Error("More than one pending tape control operation");
|
|
}
|
|
|
|
this.pendingCallee = callee;
|
|
this.pendingArgs = args;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.dequeuePendingOperation = function dequeuePendingOperation() {
|
|
/* Dequeues and reinitiates a pending tape operation */
|
|
var args = this.pendingArgs; // pending Arguments object
|
|
var callee = this.pendingCallee; // pending method to call
|
|
|
|
this.pendingCallee = this.pendingArgs = null;
|
|
callee.apply(this, args);
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.loadCommand = function loadCommand(dReg, callee, args) {
|
|
/* If the control unit or the tape unit addressed by the unit field in dReg
|
|
are currently busy, queues the args parameter (an Arguments object) in
|
|
this.pendingCallee and -Args, and returns false. If the control is idle but
|
|
the tape unit is not ready or not present, or two units have the same designate,
|
|
calls this.releaseProcessor and returns false. If the control and tape unit
|
|
are ready for their next operation, loads the contents of the processor's
|
|
D register passed to the operation routines into the T, C, and MISC registers.
|
|
Sets this.unitNr, and this.unitIndex from the digits in T. Sets this.
|
|
currentUnit to the current tape unit object. Then returns true to inidicate
|
|
the I/O can proceed */
|
|
var c; // scratch
|
|
var proceed = false; // return value: true => proceed with I/O
|
|
var t = dReg%0x10000000000; // temp to partition fields of Processor's D register
|
|
var ux; // internal unit index
|
|
|
|
//console.log(this.mnemonic + " loadCommand: " + dReg.toString(16));
|
|
if (this.controlBusy) {
|
|
this.queuePendingOperation(callee, args);
|
|
} else {
|
|
this.T = t;
|
|
this.regT.update(this.T);
|
|
this.unitNr = (t - t%0x1000000000)/0x1000000000;
|
|
t = (t - t%0x10000)/0x10000;
|
|
c = t%0x10; // low-order digit of op code
|
|
t = (t - t%0x100)/0x100; // control digits from instruction
|
|
this.C = this.unitNr*0x100000 + t*0x10 + c;
|
|
this.regC.update(this.C);
|
|
this.clearMisc();
|
|
this.unitIndex = ux = this.findDesignate(this.unitNr);
|
|
if (ux < 0) {
|
|
this.reportStatus(this.driveState.driveNotReady); // drive not ready, not present
|
|
this.queuePendingOperation(callee, args);
|
|
} else {
|
|
this.currentUnit = this.tapeUnit[ux];
|
|
if (this.currentUnit.busy || this.currentUnit.rewindLock) {
|
|
this.queuePendingOperation(callee, args);
|
|
} else {
|
|
proceed = true;
|
|
this.driveState.startTime = performance.now();
|
|
this.driveState.completionDelay = 0;
|
|
this.driveState.state = this.driveState.driveNoError;
|
|
}
|
|
}
|
|
}
|
|
|
|
return proceed;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.releaseControl = function releaseControl(param) {
|
|
/* Releases the busy status of the control. If an error is present, sets the
|
|
bits in the MISC register and the Processor's Magnetic Tape Check alarm, as
|
|
appropriate. If another operation is pending, initiates that operation.
|
|
Returns but does not use its parameter so that it can be used with
|
|
Promise.then() */
|
|
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(0);
|
|
this.controlBusy = false;
|
|
if (this.driveState.state != this.driveState.driveNoError) {
|
|
this.currentUnit.releaseUnit(this.driveState);
|
|
this.reportStatus(this.driveState.state);
|
|
}
|
|
|
|
if (this.pendingCallee !== null) {
|
|
this.dequeuePendingOperation();
|
|
}
|
|
|
|
return param;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.cancelIO = function cancelIO(param) {
|
|
/* Terminates the current I/O operation by releasing tape unit, and tape
|
|
control unit. Returns but does not use its parameter so it can be used
|
|
with Promise.then() */
|
|
|
|
//this.releaseProcessor(false, 0); // disabled 2017-12-21
|
|
this.currentUnit.releaseUnit();
|
|
this.releaseControl();
|
|
return param;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.tapeUnitFinished = function tapeUnitFinished(param) {
|
|
/* Call-back function passed to tape unit methods to signal when the unit has
|
|
completed its asynchronous operation. Returns but does not use "param", so
|
|
that it can be used with Promise.then() */
|
|
|
|
if (!this.controlBusy) { // if the control unit is currently idle...
|
|
if (this.pendingCallee !== null) {
|
|
this.dequeuePendingOperation();
|
|
}
|
|
}
|
|
|
|
return param;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.switch_Click = function switch_Click(ev) {
|
|
/* Handle the click event for buttons and switches */
|
|
|
|
switch(ev.target.id) {
|
|
case "ClearBtn":
|
|
this.clearUnit();
|
|
break;
|
|
case "Misc_RightClear":
|
|
this.clearMisc();
|
|
break;
|
|
case "C_RightClear":
|
|
this.C = 0;
|
|
this.regC.update(0);
|
|
break;
|
|
case "T_RightClear":
|
|
this.T = 0;
|
|
this.regT.update(0);
|
|
break;
|
|
} // switch target.id
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.beforeUnload = function beforeUnload(ev) {
|
|
var msg = "Closing this window will make the panel unusable.\n" +
|
|
"Suggest you stay on the page and minimize this window instead";
|
|
|
|
ev.preventDefault();
|
|
ev.returnValue = msg;
|
|
return msg;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.magTapeOnLoad = function magTapeOnLoad(ev) {
|
|
/* Initializes the MagTape Control window and user interface */
|
|
var body;
|
|
var box;
|
|
var e;
|
|
var x;
|
|
|
|
this.doc = ev.target;
|
|
this.window = this.doc.defaultView;
|
|
body = this.$$("PanelSurface");
|
|
|
|
// Misc Register
|
|
this.regMisc = new PanelRegister(this.$$("MiscRegPanel"), 4*4, 4, "Misc_", " ");
|
|
this.regMisc.lamps[15].setCaption("MCL", true);
|
|
this.regMisc.lamps[14].setCaption("MC6", true);
|
|
this.TYC1Lamp = this.regMisc.lamps[13]; // Yozzle toggle 1
|
|
this.TYC1Lamp.setCaption("TYC", true);
|
|
this.TYC2Lamp = this.regMisc.lamps[12]; // Yozzle toggle 2
|
|
this.TYC2Lamp.setCaption("TYC", true);
|
|
this.TCFLamp = this.regMisc.lamps[10]; // Preface compare failure: not in this register on a 220
|
|
this.TCFLamp.setCaption("TCF", true);
|
|
this.TFLamp = this.regMisc.lamps[9]; // Tape forward: not in this register on a 220
|
|
this.TFLamp.setCaption("TF", true);
|
|
this.TBLamp = this.regMisc.lamps[8]; // Tape bacward: not in this register on a 220
|
|
this.TBLamp.setCaption("TB", true);
|
|
this.TPCLamp = this.regMisc.lamps[7]; // Preface check
|
|
this.TPCLamp.setCaption("TPC", true);
|
|
this.regMisc.lamps[6].setCaption("TSX", true);
|
|
this.regMisc.lamps[5].setCaption("1R6", true);
|
|
this.TX1Lamp = this.regMisc.lamps[4]; // TX register
|
|
this.TX1Lamp.setCaption("TX1", true);
|
|
this.TX10Lamp = this.regMisc.lamps[3];
|
|
this.TX10Lamp.setCaption("TX10", true);
|
|
this.TX8Lamp = this.regMisc.lamps[2];
|
|
this.TX8Lamp.setCaption("TX8", true);
|
|
this.TX4Lamp = this.regMisc.lamps[1];
|
|
this.TX4Lamp.setCaption("TX4", true);
|
|
this.TX2Lamp = this.regMisc.lamps[0];
|
|
this.TX2Lamp.setCaption("TX2", true);
|
|
|
|
// Full Registers
|
|
this.regC = new PanelRegister(this.$$("CRegPanel"), 6*4, 4, "C_", "C");
|
|
this.regT = new PanelRegister(this.$$("TRegPanel"), 10*4, 4, "T_", "T");
|
|
|
|
|
|
// Events
|
|
this.window.addEventListener("beforeunload", B220MagTapeControl.prototype.beforeUnload, false);
|
|
this.$$("ClearBtn").addEventListener("click", this.boundSwitch_Click, false);
|
|
this.regMisc.rightClearBar.addEventListener("click", this.boundSwitch_Click, false);
|
|
this.regC.rightClearBar.addEventListener("click", this.boundSwitch_Click, false);
|
|
this.regT.rightClearBar.addEventListener("click", this.boundSwitch_Click, false);
|
|
|
|
this.clearUnit();
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.scan = function scan(dReg, bReg) {
|
|
/* Scans a tape unit for a block with a word matching the word at the
|
|
operand address in memory, which is stored in the TCU's T register. "bReg"
|
|
is the contents of the B register for a partial-word match, or 0 for a
|
|
full-word match. The index of the category word in the block to be compared
|
|
to the scan key is determined from dReg:41. This routine is used by MTC and
|
|
MFC */
|
|
var laneNr = 0; // lane number from TCU C register
|
|
var searchWord = 0; // target word to scan for
|
|
var wordIndex = 0; // index of category word
|
|
|
|
var scanForward = () => {
|
|
/* Handles a block after it has been scanned in a forward direction.
|
|
If the category word is not equal, continues scanning in a forward
|
|
direction; otherwise, repositions to allow the matching block to be read
|
|
next. If an EOT or control block is encountered, the control word from
|
|
that block is ignored, and the tape is then repositioned, ready to read
|
|
the EOT or control block. The drive forward scan stops in the erase gap
|
|
of the block containing the category or control word, so a reposition
|
|
backs up to allow reading the next block. A second reposition is needed
|
|
is needed to back up into the prior block, allowing the block just
|
|
scanned to be read next */
|
|
var compare = 0; // category word field comparison result
|
|
|
|
if (this.driveState.state == this.driveState.driveHasControlWord) {
|
|
// EOT or control block encountered: terminate the I/O
|
|
this.driveState.state = this.driveState.driveNoError;
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(1);
|
|
this.currentUnit.reposition(this.driveState) // reposition into the EOT block
|
|
.then(this.currentUnit.boundReposition) // reposition again into prior block
|
|
.then(this.currentUnit.boundReleaseDelay) // allowing the EOT block to be read next
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
} else {
|
|
// Compare category word from tape with T register
|
|
compare = this.compareKeywordField(this.driveState.keyword);
|
|
if (compare != 0) { // if category word unequal, keep scanning
|
|
this.currentUnit.scanBlock(this.driveState, wordIndex)
|
|
.then(scanForward)
|
|
.catch(this.boundReleaseControl);
|
|
} else { // Keyword was equal: stop and reposition
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(1);
|
|
this.currentUnit.reposition(this.driveState) // reposition into the matching block
|
|
.then(this.currentUnit.boundReposition) // reposition again into prior block
|
|
.then(this.currentUnit.boundReleaseDelay) // allowing the matching block to be read next
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
}
|
|
};
|
|
|
|
var scanFirstBlock = (driveState) => {
|
|
/* Closure to call u.scanBlock with additional parameters from Promise.then() */
|
|
|
|
this.currentUnit.scanBlock(this.driveState, wordIndex);
|
|
};
|
|
|
|
if (this.loadCommand(dReg, scan, arguments)) {
|
|
this.controlBusy = true;
|
|
this.driveState.completionDelay = 16;
|
|
|
|
// Check for field compare validity
|
|
if (this.determineFieldCompare(bReg)) {
|
|
this.releaseProcessor(false, 0);
|
|
this.releaseControl();
|
|
} else {
|
|
// Fetch the scan target word from memory
|
|
searchWord = this.fetchWord(false);
|
|
if (searchWord < 0) {
|
|
this.driveState.state = this.driveState.driveMemoryError;
|
|
this.releaseProcessor(false, 0);
|
|
this.releaseControl();
|
|
} else {
|
|
// Start the scan after changing lane, as appropriate
|
|
this.T = searchWord%0x10000000000;
|
|
this.regT.update(this.T);
|
|
this.releaseProcessor(false, 0);
|
|
this.TFLamp.set(1);
|
|
laneNr = ((this.C - this.C%0x100)/0x100)%2;
|
|
wordIndex = ((this.C - this.C%0x10)/0x10)%2;
|
|
if (wordIndex == 0) {
|
|
wordIndex = 10;
|
|
}
|
|
|
|
this.currentUnit.setLane(laneNr, this.driveState)
|
|
.then(this.currentUnit.boundStartUpForward)
|
|
.then(scanFirstBlock)
|
|
.then(scanForward)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.search = function search(dReg, bReg) {
|
|
/* Searches a tape unit for a block with a keyword matching the word at the
|
|
operand address in memory, which is stored in the TCU's T register. "bReg"
|
|
is the contents of the B register for a partial-word match, or 0 for a
|
|
full-word match. This routine is used by MTS and MFS */
|
|
var laneNr = 0; // lane number from TCU C register
|
|
var searchWord = 0; // target word to search for
|
|
|
|
var searchForward = () => {
|
|
/* Handles a block after it has been searched in a forward direction.
|
|
If the keyword is low, continues searching in a forward direction;
|
|
otherwise, reverses tape direction and initiates a backward search.
|
|
If an EOT block is encountered, the control word from that block is
|
|
ignored, and the tape is then repositioned, ready to read the EOT block.
|
|
The drive forward search stops in the block after the keyword, so a
|
|
reposition backs up to allow reading the block just searched */
|
|
var compare = 0; // keyword field comparison result
|
|
|
|
if (this.driveState.state == this.driveState.driveHasControlWord) {
|
|
// EOT block encountered: terminate the I/O
|
|
this.driveState.state = this.driveState.driveNoError;
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(1);
|
|
this.currentUnit.reposition(this.driveState)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
} else {
|
|
// Compare keyword from tape with T register
|
|
compare = this.compareKeywordField(this.driveState.keyword);
|
|
if (compare < 0) { // if keyword low, keep searching
|
|
this.currentUnit.searchForwardBlock(this.driveState)
|
|
.then(searchForward)
|
|
.catch(this.boundReleaseControl);
|
|
} else { // Keyword was high or equal: reverse direction
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(1);
|
|
this.currentUnit.reverseDirection(this.driveState)
|
|
.then(this.currentUnit.boundSearchBackwardBlock)
|
|
.then(searchBackward)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
}
|
|
};
|
|
|
|
var searchBackward = () => {
|
|
/* Handles a block after it has been searched in a backward direction.
|
|
If the keyword is high, continues searching in a backward direction.
|
|
If the keyword is low, reverses tape direction again and searches one
|
|
block in the forward direction. In this case, the result of the search
|
|
is ignored, leaving the tape positioned to read the next block, which
|
|
will be greater-than or equal-to the search target.
|
|
If the keyword is equal, the tape is already positioned in the prior
|
|
block, so we just quit, leaving the tape in position to read the block
|
|
with the equal key */
|
|
var compare = this.compareKeywordField(this.driveState.keyword);
|
|
|
|
if (compare > 0) { // keyword is high, continue searching...
|
|
this.currentUnit.searchBackwardBlock(this.driveState)
|
|
.then(searchBackward)
|
|
.catch(this.boundReleaseControl);
|
|
} else if (compare < 0) { // keyword is low, reverse direction, search one block, and quit
|
|
this.TBLamp.set(0);
|
|
this.TFLamp.set(1);
|
|
this.currentUnit.reverseDirection(this.driveState)
|
|
.then(this.currentUnit.boundSearchForwardBlock)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
} else { // keyword equal, just quit with tape positioned in prior block
|
|
this.currentUnit.releaseDelay(this.driveState)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
if (this.loadCommand(dReg, search, arguments)) {
|
|
this.controlBusy = true;
|
|
this.driveState.completionDelay = 16;
|
|
|
|
// Check for field compare validity
|
|
if (this.determineFieldCompare(bReg)) {
|
|
this.releaseProcessor(false, 0);
|
|
this.releaseControl();
|
|
} else {
|
|
// Fetch the search target word from memory
|
|
searchWord = this.fetchWord(false);
|
|
if (searchWord < 0) {
|
|
this.driveState.state = this.driveState.driveMemoryError;
|
|
this.releaseProcessor(false, 0);
|
|
this.releaseControl();
|
|
} else {
|
|
// Start the search after changing lane, as appropriate
|
|
this.T = searchWord%0x10000000000;
|
|
this.regT.update(this.T);
|
|
this.releaseProcessor(false, 0);
|
|
this.TFLamp.set(1);
|
|
laneNr = ((this.C - this.C%0x100)/0x100)%2;
|
|
|
|
this.currentUnit.setLane(laneNr, this.driveState)
|
|
.then(this.currentUnit.boundStartUpForward)
|
|
.then(this.currentUnit.boundSearchForwardBlock)
|
|
.then(searchForward)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.read = function read(dReg, record) {
|
|
/* Reads the number of blocks indicated in dReg. If "record" is true (MRR),
|
|
block lengths (preface words) are stored into the word in memory preceding
|
|
the data read from tape. This routine is used by MRD and MRR */
|
|
var controlEnabled = false; // true => control blocks will be recognized
|
|
|
|
var readBlock = () => {
|
|
/* Reads blocks on the designated unit until the block count is
|
|
exhausted or some error occurs. If an EOT block or control block
|
|
is encountered, the drive returns the control word from that block and
|
|
the I/O is terminated normally after passing the control word to the
|
|
Processor for action. The tape is repositioned, ready to read the next
|
|
block */
|
|
|
|
if (this.driveState.state == this.driveState.driveHasControlWord) {
|
|
this.driveState.state = this.driveState.driveNoError;
|
|
this.releaseProcessor(true, this.driveState.controlWord);
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(1);
|
|
this.currentUnit.reposition(this.driveState)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
} else if (this.decrementBlockCount()) {
|
|
this.currentUnit.readNextBlock(this.driveState, record, controlEnabled, this.boundStoreWord)
|
|
.then(readBlock)
|
|
.catch(this.boundCancelIO);
|
|
} else { // block count exhausted
|
|
this.releaseProcessor(false, 0);
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(1);
|
|
this.currentUnit.reposition(this.driveState)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
var readFirstBlock = (driveState) => {
|
|
/* Closure to call u.readNextBlock with additional parameters from Promise.then() */
|
|
|
|
this.currentUnit.readNextBlock(this.driveState, record, controlEnabled, this.boundStoreWord);
|
|
};
|
|
|
|
if (this.loadCommand(dReg, read, arguments)) {
|
|
this.controlBusy = true;
|
|
this.driveState.completionDelay = 18;
|
|
controlEnabled = (this.C%0x20 < 0x10); // low-order bit of instruction v-digit
|
|
this.TFLamp.set(1);
|
|
|
|
this.currentUnit.startUpForward(this.driveState)
|
|
.then(readFirstBlock)
|
|
.then(readBlock)
|
|
.catch(this.boundCancelIO);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.overwrite = function overwrite(dReg, record) {
|
|
/* Overwrites the number of blocks and of the size indicated in dReg. If
|
|
"record" is true (MOR), block lengths (preface words) are taken from the
|
|
word in memory preceding the data to be written. Otherwise, block lengths
|
|
are taken from the instruction control digits. This routine is used by
|
|
MOW and MOR */
|
|
var blocksLeft = true; // true => more blocks to process
|
|
|
|
var writeBlock = () => {
|
|
/* Overwrites blocks on the designated unit until the block count is
|
|
exhausted or some error occurs. If an EOT block with a preface mismatch
|
|
is encountered, the drive returns the control word from that block and
|
|
the I/O is terminated normally after passing the control word to the
|
|
Processor for action. The tape is repositioned, ready to read the next
|
|
block */
|
|
var words = 0;
|
|
|
|
if (this.driveState.state == this.driveState.driveHasControlWord) {
|
|
this.driveState.state = this.driveState.driveNoError;
|
|
this.releaseProcessor(true, this.driveState.controlWord);
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(1);
|
|
this.currentUnit.reposition(this.driveState)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
} else if (blocksLeft) {
|
|
words = this.determineBlockLength(record);
|
|
if (words < 0) {
|
|
this.cancelIO();
|
|
} else {
|
|
blocksLeft = this.decrementBlockCount();
|
|
this.currentUnit.overwriteBlock(this.driveState, record, words, this.boundFetchWord)
|
|
.then(writeBlock)
|
|
.catch(this.boundCancelIO);
|
|
}
|
|
} else { // block count exhausted
|
|
this.releaseProcessor(false, 0);
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(1);
|
|
this.currentUnit.reposition(this.driveState)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
if (this.loadCommand(dReg, overwrite, arguments)) {
|
|
this.controlBusy = true;
|
|
this.driveState.completionDelay = 18;
|
|
this.TFLamp.set(1);
|
|
|
|
this.currentUnit.startUpForward(this.driveState)
|
|
.then(writeBlock)
|
|
.catch(this.boundCancelIO);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.initialWrite = function initialWrite(dReg, record) {
|
|
/* Initial-writes the number of blocks and of the size indicated in dReg.
|
|
If "record" is true (MIR), block lengths (preface words) are taken from the
|
|
word in memory preceding the data to be written. Otherwise, block lengths
|
|
are taken from the instruction control digits. This routine is used by
|
|
MIW and MIR */
|
|
var blocksLeft = true; // true => more blocks to process
|
|
|
|
var writeBlock = () => {
|
|
/* Initial-writes blocks on the designated unit until the block count is
|
|
exhausted or some error occurs */
|
|
var words = 0;
|
|
|
|
if (blocksLeft) {
|
|
words = this.determineBlockLength(record);
|
|
if (words < 0) {
|
|
this.cancelIO();
|
|
} else {
|
|
blocksLeft = this.decrementBlockCount();
|
|
this.currentUnit.initialWriteBlock(this.driveState, record, words, this.boundFetchWord)
|
|
.then(writeBlock)
|
|
.catch(this.boundCancelIO);
|
|
}
|
|
} else { // block count exhausted
|
|
this.releaseProcessor(false, 0);
|
|
this.TFLamp.set(0); // direction actually changes after WriteFinalize
|
|
this.TBLamp.set(1); // but that's messy to do here...
|
|
this.currentUnit.initialWriteFinalize(this.driveState)
|
|
.then(this.currentUnit.boundReposition)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
if (this.loadCommand(dReg, initialWrite, arguments)) {
|
|
this.controlBusy = true;
|
|
this.driveState.completionDelay = 20;
|
|
this.TFLamp.set(1);
|
|
|
|
this.currentUnit.startUpForward(this.driveState)
|
|
.then(writeBlock)
|
|
.catch(this.boundCancelIO);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.positionForward = function positionForward(dReg) {
|
|
/* Positions the tape forward the number of blocks indicated in dReg */
|
|
|
|
var spaceBlock = () => {
|
|
/* Spaces forward over blocks on the designated unit until the block
|
|
count is exhausted or some error (like end-of-tape) occurs */
|
|
|
|
if (this.decrementBlockCount()) {
|
|
this.currentUnit.spaceForwardBlock(this.driveState)
|
|
.then(spaceBlock)
|
|
.catch(this.boundReleaseControl);
|
|
} else { // block count exhausted
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(1);
|
|
this.currentUnit.reposition(this.driveState)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
if (this.loadCommand(dReg, positionForward, arguments)) {
|
|
this.controlBusy = true;
|
|
this.driveState.completionDelay = 16;
|
|
this.releaseProcessor(false, 0);
|
|
this.TFLamp.set(1);
|
|
|
|
this.currentUnit.startUpForward(this.driveState)
|
|
.then(this.currentUnit.boundSpaceForwardBlock)
|
|
.then(spaceBlock)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.positionBackward = function positionBackward(dReg) {
|
|
/* Positions the tape backward the number of blocks indicated in dReg */
|
|
|
|
var spaceBlock = () => {
|
|
/* Spaces backward over blocks on the designated unit until the block
|
|
count is exhausted or some error (like beginning-of-tape) occurs */
|
|
|
|
if (this.decrementBlockCount()) {
|
|
this.currentUnit.spaceBackwardBlock(this.driveState)
|
|
.then(spaceBlock)
|
|
.catch(this.boundReleaseControl);
|
|
} else { // block count exhausted
|
|
this.currentUnit.reposition(this.driveState)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
if (this.loadCommand(dReg, positionBackward, arguments)) {
|
|
this.controlBusy = true;
|
|
this.driveState.completionDelay = 6;
|
|
this.releaseProcessor(false, 0);
|
|
this.TBLamp.set(1);
|
|
|
|
this.currentUnit.startUpBackward(this.driveState)
|
|
.then(this.currentUnit.boundSpaceBackwardBlock)
|
|
.then(spaceBlock)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.positionAtEnd = function positionAtEnd(dReg) {
|
|
/* Positions the tape to the end of recorded information (i.e., when a gap
|
|
longer than inter-block gap is detected. Leaves the tape at the end of the
|
|
prior recorded block */
|
|
|
|
var spaceBlock = () => {
|
|
/* Spaces over blocks on the designated unit until end-of-information is
|
|
detected or some error (like end-of-tape) occurs */
|
|
|
|
if (this.driveState.state != this.driveState.driveAtEOI) {
|
|
this.currentUnit.spaceEOIBlock(this.driveState)
|
|
.then(spaceBlock)
|
|
.catch(this.boundReleaseControl);
|
|
} else { // found end-of-information
|
|
this.driveState.state = this.driveState.driveNoError;
|
|
this.TFLamp.set(0);
|
|
this.TBLamp.set(1);
|
|
this.currentUnit.reposition(this.driveState)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
if (this.loadCommand(dReg, positionAtEnd, arguments)) {
|
|
this.controlBusy = true;
|
|
this.driveState.completionDelay = 23;
|
|
this.releaseProcessor(false, 0);
|
|
this.TFLamp.set(1);
|
|
|
|
this.currentUnit.startUpForward(this.driveState)
|
|
.then(this.currentUnit.boundSpaceEOIBlock)
|
|
.then(spaceBlock)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.laneSelect = function laneSelect(dReg) {
|
|
/* Selects the tape lane of the designated unit. Returns an alarm if the
|
|
unit does not exist or is not ready */
|
|
var laneNr; // lane to select (0, 1)
|
|
|
|
if (this.loadCommand(dReg, laneSelect, arguments)) {
|
|
this.controlBusy = true;
|
|
laneNr = ((this.C - this.C%0x100)/0x100)%2;
|
|
this.driveState.completionDelay = 3;
|
|
this.fetchWord(true); // memory access for MTS/MFS not used by MLS
|
|
this.releaseProcessor(false, 0);
|
|
|
|
this.currentUnit.laneSelect(this.driveState, laneNr)
|
|
.then(this.currentUnit.boundReleaseDelay)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundReleaseControl)
|
|
.catch(this.boundReleaseControl);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.rewind = function rewind(dReg) {
|
|
/* Initiates rewind of the designated unit. Returns an alarm if the unit
|
|
does not exist or is not ready */
|
|
var laneNr; // lane to select (0, 1)
|
|
var lockout; // lockout after rewind (0, 1)
|
|
|
|
if (this.loadCommand(dReg, rewind, arguments)) {
|
|
this.controlBusy = true;
|
|
laneNr = ((this.C - this.C%0x100)/0x100)%2;
|
|
lockout = ((this.C - this.C%0x10)/0x10)%2;
|
|
this.fetchWord(true); // memory access for MTS/MFS not used by MRW/MDA
|
|
this.releaseProcessor(false, 0);
|
|
this.TBLamp.set(1);
|
|
setCallback(this.mnemonic, this, 50, this.releaseControl, this.driveState);
|
|
|
|
this.currentUnit.rewind(this.driveState, laneNr, lockout)
|
|
.then(this.currentUnit.boundReleaseUnit)
|
|
.then(this.boundTapeUnitFinished);
|
|
}
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.testUnitReady = function testUnitReady(dReg) {
|
|
/* Interrogates status of the designated unit. Returns true if ready */
|
|
var result = false; // return value
|
|
var ux; // internal unit index
|
|
|
|
ux = ((dReg - dReg%0x1000000000)/0x1000000000)%0x10;
|
|
ux = this.findDesignate(ux);
|
|
if (ux >= 0) {
|
|
if (this.tapeUnit[ux].ready) {
|
|
if (!this.tapeUnit[ux].busy) {
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.testUnitAtEOT = function testUnitAtEOT(dReg) {
|
|
/* Interrogates status of the designated unit. Returns true if ready and at
|
|
end-of-tape */
|
|
var result = false; // return value
|
|
var ux; // internal unit index
|
|
|
|
ux = ((dReg - dReg%0x1000000000)/0x1000000000)%0x10;
|
|
ux = this.findDesignate(ux);
|
|
if (ux >= 0) {
|
|
if (this.tapeUnit[ux].ready) {
|
|
if (this.tapeUnit[ux].atEOT) {
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.clearUnit = function clearUnit() {
|
|
/* Clears the internal state of the control unit */
|
|
|
|
this.clear();
|
|
this.clearMisc();
|
|
this.regC.update(this.C);
|
|
this.regT.update(this.T);
|
|
};
|
|
|
|
/**************************************/
|
|
B220MagTapeControl.prototype.shutDown = function shutDown() {
|
|
/* Shuts down the panel */
|
|
var x;
|
|
|
|
if (this.tapeUnit) {
|
|
for (x=this.tapeUnit.length-1; x>=0; --x) {
|
|
if (this.tapeUnit[x]) {
|
|
this.tapeUnit[x].shutDown();
|
|
this.tapeUnit[x] = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.window.removeEventListener("beforeunload", B220MagTapeControl.prototype.beforeUnload, false);
|
|
this.$$("ClearBtn").removeEventListener("click", this.boundSwitch_Click, false);
|
|
this.regMisc.rightClearBar.removeEventListener("click", this.boundSwitch_Click, false);
|
|
this.regC.rightClearBar.removeEventListener("click", this.boundSwitch_Click, false);
|
|
this.regT.rightClearBar.removeEventListener("click", this.boundSwitch_Click, false);
|
|
this.window.close();
|
|
}; |