mirror of
https://github.com/pkimpel/retro-220.git
synced 2026-02-10 18:20:44 +00:00
1182 lines
48 KiB
JavaScript
1182 lines
48 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 = 432; // 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.boundReleaseDelay(this.driveState)
|
|
.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();
|
|
}; |