1
0
mirror of https://github.com/pkimpel/retro-220.git synced 2026-01-13 15:18:24 +00:00

Commit version 0.03b: continue development of magnetic tape drive.

This commit is contained in:
Paul Kimpel 2017-11-02 12:45:33 -07:00
parent d518d6e044
commit 4c63d98515
4 changed files with 1261 additions and 850 deletions

View File

@ -243,8 +243,8 @@ function B220Processor(config, devices) {
this.boundCardatronReceiveWord = B220Processor.bindMethod(this, B220Processor.prototype.cardatronReceiveWord);
this.boundMagTapeComplete = B220Processor.bindMethod(this, B220Processor.prototype.magTapeComplete);
this.boundMagTapeReceiveBlock = B220Processor.bindMethod(this, B220Processor.prototype.magTapeReceiveBlock);
this.boundMagTapeSendBlock = B220Processor.bindMethod(this, B220Processor.prototype.magTapeSendBlock);
this.boundMagTapeReceiveWord = B220Processor.bindMethod(this, B220Processor.prototype.magTapeReceiveWord);
this.boundMagTapeSendWord = B220Processor.bindMethod(this, B220Processor.prototype.magTapeSendWord);
this.clear(); // Create and initialize the processor state
@ -256,7 +256,7 @@ function B220Processor(config, devices) {
* Global Constants *
***********************************************************************/
B220Processor.version = "0.03a";
B220Processor.version = "0.03b";
B220Processor.tick = 1000/200000; // milliseconds per clock cycle (200KHz)
B220Processor.cyclesPerMilli = 1/B220Processor.tick;
@ -1886,7 +1886,7 @@ B220Processor.prototype.compareField = function compareField() {
if (s == 0) {
s = 10;
}
L = (this.CCONTROL >>> 8) & 0x0F;
L = (this.CCONTROL >>> 8)%0x10;
if (L == 0) {
L = 10;
}
@ -2009,7 +2009,7 @@ B220Processor.prototype.increaseFieldLocation = function increaseFieldLocation()
if (s == 0) {
s = 10;
}
L = (this.CCONTROL >>> 8) & 0x0F;
L = (this.CCONTROL >>> 8)%0x10;
if (L == 0) {
L = 10;
}
@ -2092,7 +2092,7 @@ B220Processor.prototype.decreaseFieldLocation = function decreaseFieldLocation(l
if (s == 0) {
s = 10;
}
L = (this.CCONTROL >>> 8) & 0x0F;
L = (this.CCONTROL >>> 8)%0x10;
if (L == 0) {
L = 10;
}
@ -2173,7 +2173,7 @@ B220Processor.prototype.branchField = function branchField(regValue) {
if (s == 0) {
s = 10;
}
L = (this.CCONTROL >>> 8) & 0x0F;
L = (this.CCONTROL >>> 8)%0x10;
if (L == 0) {
L = 10;
}
@ -2265,7 +2265,7 @@ B220Processor.prototype.storeRegister = function storeRegister() {
if (s == 0) {
s = 10;
}
L = (this.CCONTROL >>> 8) & 0x0F;
L = (this.CCONTROL >>> 8)%0x10;
if (L == 0) {
L = 10;
}
@ -2433,7 +2433,7 @@ B220Processor.prototype.consoleOutputSign = function consoleOutputSign(printSign
this.ioComplete(true);
} else {
this.D.set(this.IB.value);
this.opTime += 0.070; // estimate for memory access and rotation
this.execClock += 0.070; // estimate for memory access and rotation
w = this.D.value%0x10000000000;
d = (this.D.value - w)/0x10000000000; // get the sign digit
this.D.set(w*0x10 + d); // rotate D+sign left one
@ -2470,7 +2470,7 @@ B220Processor.prototype.consoleOutputChar = function consoleOutputChar(printChar
w = this.D.value % 0x1000000000;
d = (this.D.value - w)/0x1000000000; // get next 2 digits
this.D.set(w*0x100 + d); // rotate D+sign left by two
this.opTime += 0.060; // estimate for rotation
this.execClock += 0.060; // estimate for rotation
this.DC.inc(); // increment DC for two digits
this.DC.inc();
this.PA.set(d);
@ -2495,7 +2495,7 @@ B220Processor.prototype.consoleOutputChar = function consoleOutputChar(printChar
w = this.D.value % 0x10000000000;
d = (this.D.value - w)/0x10000000000; // get a digit
this.D.value = w*0x10 + d; // rotate D+sign left by one
this.opTime += 0.065; // estimate for rotation
this.execClock += 0.065; // estimate for rotation
this.DC.inc();
} while (d == 0 && this.LT1.value && this.DC.value < 0x20);
@ -2727,13 +2727,14 @@ B220Processor.prototype.cardatronOutputWord = function cardatronOutputWord() {
} else if (this.MET.value) { // previous memory access error
word = 0;
} else {
this.opTime += 0.117; // time for full-word transfer
word = this.readMemory(); // address in E was previously set
if (this.MET.value) {
word = 0;
} else {
this.E.dec(); // step down to next memory address
}
this.execClock += 0.117; // time for full-word transfer
}
return word;
@ -2771,7 +2772,6 @@ B220Processor.prototype.cardatronReceiveWord = function cardatronReceiveWord(wor
// Memory error has occurred: just ignore further data from Cardatron
} else {
// Full word accumulated -- process it and initialize for the next word
this.opTime += 0.117; // time for full-word transfer
this.D.set(word);
word %= 0x10000000000; // strip the sign digit
sign = (this.D.value - word)/0x10000000000; // get D-sign
@ -2811,8 +2811,8 @@ B220Processor.prototype.cardatronReceiveWord = function cardatronReceiveWord(wor
default: // sign is 8, 9: store word with optional B mod
if (!(this.rDigit & 0x08)) { // no B-register modification
this.IB.set(this.D.value);
} else { // add B to low-order five digits of word
word = word - word%0x100000 + this.bcdAdd(word, this.B.value, 5);
} else { // add B to low-order four digits of word
word = word - word%0x100000 + this.bcdAdd(word, this.B.value, 4);
this.C10.set(0); // reset carry toggle
this.IB.set((sign%2)*0x10000000000 + word);
}
@ -2822,6 +2822,8 @@ B220Processor.prototype.cardatronReceiveWord = function cardatronReceiveWord(wor
}
break;
} // switch sign
this.execClock += 0.117; // time for full-word transfer
}
return returnCode;
@ -2864,139 +2866,81 @@ B220Processor.prototype.magTapeComplete = function magTapeComplete(alarm, contro
};
/**************************************/
B220Processor.prototype.magTapeSendBlock = function magTapeSendBlock(buffer, words) {
/* Sends a block of data from memory to the tape control unit. "buffer" is an
array of words to receive the data to be written to tape. "words" is the number
of words to place in the buffer, starting at the current operand address in the
C register. Returns true if the processor has been cleared or a memory address
error occurs, and the I/O must be aborted */
var result = false; // return value
var that = this; // local context
var x = 0; // buffer index
//console.log("TSU " + this.selectedUnit + " W, Len " + words +
// ", ADDR=" + this.CADDR.toString(16));
B220Processor.prototype.magTapeSendWord = function magTapeSendWord(initial) {
/* Sends the next of data from memory to the tape control unit, starting at
the current operand address in the C register. "initial" is true if this
call is the first to fetch words for a block. This causes the routine to
save the current operand address in the control digits of C. Returns
binary -1 if the processor has been cleared or a memory address error
occurs, and the I/O must be aborted. Returns the BCD memory word otherwise */
var result; // return value
if (!this.AST.value) {
result = true;
result = -1; // we've probably been cleared
} else {
while (x < words) {
this.E.set(this.CADDR);
this.CADDR = this.bcdAdd(this.CADDR, 1, 4);
this.readMemory();
if (this.MET.value) { // invalid address
result = true;
break; // out of do-loop
} else {
buffer[x] = this.IB.value;
++x;
}
if (initial) {
this.clockIn();
this.CCONTROL = this.CADDR; // copy C address into control digits
}
this.E.set(this.CADDR);
this.CADDR = this.bcdAdd(this.CADDR, 1, 4);
this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR);
this.readMemory();
if (this.MET.value) { // invalid address
result = -1;
} else {
result = this.IB.value;
this.D.set(result);
this.execClock += 0.480; // time to transfer one word to tape
}
}
this.C.set(this.C.value - this.C.value%0x10000 + this.CADDR);
return result;
};
/**************************************/
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, schedule()
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;
B220Processor.prototype.magTapeReceiveWord = function magTapeReceiveWord(initial, word) {
/* Stores the next of data from the tape control unit to memory, starting at
the current operand address in the C register. "initial" is true if this
call is the first to store words for a block. This causes the routine to
save the current operand address in the control digits of C. Returns
binary -1 if the processor has been cleared or a memory address error
occurs, and the I/O must be aborted. Returns 0 otherwise */
var result = 0; // return value
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.schedule();
}
} else {
// Flip the loop buffer toggles
that.togMT1BV5 = that.togMT1BV4;
that.togMT1BV4 = 1-that.togMT1BV4;
// Suspend time again during I/O
that.execTime -= 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;
if (!this.AST.value) {
result = -1; // we've probably been cleared
} else {
this.execTime += 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 (initial) {
this.clockIn();
this.CCONTROL = this.CADDR; // copy C address into control digits
}
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.set(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, 11);
} else {
w = this.bcdAdd(w, this.B.value, 11);
}
loop[x] = w;
} // for x
this.A.set(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();
this.E.set(this.CADDR);
this.CADDR = this.bcdAdd(this.CADDR, 1, 4);
this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR);
this.D.set(word);
if (this.vDigit & 0x08) { // B-adjustment of words is enabled
sign = (word - word%0x10000000000);
if (sign & 0x08) { // this word is to be B-adjusted
word = (sign&0x07)*0x10000000000 + word%0x10000000000 -
word%0x100000 + this.bcdAdd(word, this.B.value, 4);
this.C10.set(0); // reset carry toggle
}
}
this.IB.set(word);
this.writeMemory();
if (this.MET.value) { // invalid address
result = -1;
} else {
this.execClock += 0.480; // time to transfer one word to tape
}
}
return aborted;
return result;
};
@ -3109,7 +3053,7 @@ B220Processor.prototype.execute = function execute() {
}
this.selectedUnit = d;
this.rDigit = this.CCONTROL & 0x0F;
this.rDigit = this.CCONTROL%0x10;
this.sDigit = 1; // use word count in C (32)
this.D.set(0);
this.ioInitiate();
@ -3653,25 +3597,20 @@ B220Processor.prototype.execute = function execute() {
this.setMagneticTapeCheck(true); // no tape control
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
switch (this.CCONTROL%0x10) {
case 0: case 1: case 2: case 3: // MTS/MFS: search or field search
this.setProgramCheck(true); // TEMP //
this.operationComplete();
break;
case 4: case 5: case 6: case 7: // MLS: lane select
this.ioInitiate();
this.magTape.laneSelect(this.D.value, this.boundMagTapeComplete);
break;
case 8: case 9: // MRW/MDA: rewind, with or without lockout
this.ioInitiate();
this.magTape.rewind(this.D.value, this.boundMagTapeComplete);
break;
default: // should never happen
this.setProgramCheck(true);
this.operationComplete();
break;
} // switch on operation variant
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.vDigit = this.CCONTROL%0x10;
this.ioInitiate();
if (this.vDigit & 0x08) { // MRW/MDA: rewind, with or without lockout
this.magTape.rewind(this.D.value, this.boundMagTapeComplete, this.boundMagTapeSendWord);
} else if (this.vDigit & 0x04) { // MLS: lane select
this.magTape.laneSelect(this.D.value, this.boundMagTapeComplete, this.boundMagTapeSendWord);
} else { // MTS/MFS: search or field search
if (this.D.value%0x80000000000 < 0x40000000000) { // full-word search
this.magTape.search(this.D.value, this.boundMagTapeComplete, 0, this.boundMagTapeSendWord);
} else { // partial-word search based on sL in B
this.magTape.search(this.D.value, this.boundMagTapeComplete, this.B.value, this.boundMagTapeSendWord);
}
}
}
break;
@ -3681,13 +3620,29 @@ B220Processor.prototype.execute = function execute() {
break;
case 0x52: //--------------------- MRD Magnetic tape read
this.setProgramCheck(1);
this.operationComplete();
this.opTime = 0.160;
if (!this.magTape) {
this.setMagneticTapeCheck(true); // no tape control
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.vDigit = this.CCONTROL%0x10;
this.ioInitiate();
this.magTape.read(this.D.value, this.boundMagTapeComplete, false, this.boundMagTapeReceiveWord);
}
break;
case 0x53: //--------------------- MRR Magnetic tape read, record
this.setProgramCheck(1);
this.operationComplete();
this.opTime = 0.160;
if (!this.magTape) {
this.setMagneticTapeCheck(true); // no tape control
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.vDigit = this.CCONTROL%0x10;
this.ioInitiate();
this.magTape.read(this.D.value, this.boundMagTapeComplete, true, this.boundMagTapeReceiveWord);
}
break;
case 0x54: //--------------------- MIW Magnetic tape initial write
@ -3696,11 +3651,9 @@ B220Processor.prototype.execute = function execute() {
this.setMagneticTapeCheck(true); // no tape control
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
this.CCONTROL = this.CADDR; // copy C address into control digits
this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR);
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.ioInitiate();
this.magTape.initialWrite(this.D.value, this.boundMagTapeComplete, this.boundMagTapeSendBlock);
this.magTape.initialWrite(this.D.value, this.boundMagTapeComplete, false, this.boundMagTapeSendWord);
}
break;
@ -3710,22 +3663,34 @@ B220Processor.prototype.execute = function execute() {
this.setMagneticTapeCheck(true); // no tape control
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
this.CCONTROL = this.CADDR; // copy C address into control digits
this.C.set((this.CCONTROL*0x100 + this.COP)*0x10000 + this.CADDR);
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.ioInitiate();
this.magTape.initialWriteRecord(this.D.value, this.boundMagTapeComplete, this.boundMagTapeSendBlock);
this.magTape.initialWrite(this.D.value, this.boundMagTapeComplete, true, this.boundMagTapeSendWord);
}
break;
case 0x56: //--------------------- MOW Magnetic tape overwrite
this.setProgramCheck(1);
this.operationComplete();
this.opTime = 0.160;
if (!this.magTape) {
this.setMagneticTapeCheck(true); // no tape control
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.ioInitiate();
this.magTape.overwrite(this.D.value, this.boundMagTapeComplete, false, this.boundMagTapeSendWord);
}
break;
case 0x57: //--------------------- MOR Magnetic tape overwrite, record
this.setProgramCheck(1);
this.operationComplete();
this.opTime = 0.160;
if (!this.magTape) {
this.setMagneticTapeCheck(true); // no tape control
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.ioInitiate();
this.magTape.overwrite(this.D.value, this.boundMagTapeComplete, true, this.boundMagTapeSendWord);
}
break;
case 0x58: //--------------------- MPF/MPB/MIE Magnetic tape position forward/backward/at end
@ -3734,7 +3699,7 @@ B220Processor.prototype.execute = function execute() {
this.setMagneticTapeCheck(true); // no tape control
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.ioInitiate();
switch (this.CCONTROL%0x10) {
case 1: // MPB: position tape backward
@ -3780,9 +3745,9 @@ B220Processor.prototype.execute = function execute() {
this.setCardatronCheck(1);
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
this.rDigit = this.CCONTROL & 0x0F;
this.vDigit = (this.CCONTROL >>> 4) & 0x0F;
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.rDigit = this.CCONTROL%0x10;
this.vDigit = (this.CCONTROL >>> 4)%0x10;
this.ioInitiate();
d = this.cardatron.inputInitiate(this.selectedUnit, this.rDigit, this.boundCardatronReceiveWord);
if (d < 0) { // invalid unit
@ -3800,9 +3765,9 @@ B220Processor.prototype.execute = function execute() {
this.setCardatronCheck(1);
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
this.rDigit = this.CCONTROL & 0x0F;
this.vDigit = (this.CCONTROL >>> 4) & 0x0F;
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.rDigit = this.CCONTROL%0x10;
this.vDigit = (this.CCONTROL >>> 4)%0x10;
this.ioInitiate();
d = this.cardatron.outputInitiate(this.selectedUnit, this.rDigit, this.vDigit,
this.boundCardatronOutputWord, this.boundCardatronOutputFinished);
@ -3821,8 +3786,8 @@ B220Processor.prototype.execute = function execute() {
this.setCardatronCheck(1);
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
this.rDigit = this.CCONTROL & 0x0F;
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.rDigit = this.CCONTROL%0x10;
this.ioInitiate();
d = this.cardatron.inputFormatInitiate(this.selectedUnit, this.rDigit,
this.boundCardatronOutputWord, this.boundCardatronOutputFinished);
@ -3841,8 +3806,8 @@ B220Processor.prototype.execute = function execute() {
this.setCardatronCheck(1);
this.operationComplete();
} else {
this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
this.rDigit = this.CCONTROL & 0x0F;
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
this.rDigit = this.CCONTROL%0x10;
this.ioInitiate();
d = this.cardatron.outputFormatInitiate(this.selectedUnit, this.rDigit,
this.boundCardatronOutputWord, this.boundCardatronOutputFinished);
@ -3860,7 +3825,7 @@ B220Processor.prototype.execute = function execute() {
if (!this.cardatron) {
this.setCardatronCheck(1);
} else {
this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
d = this.cardatron.inputReadyInterrogate(this.selectedUnit);
if (d < 0) { // invalid unit
this.setCardatronCheck(1);
@ -3879,7 +3844,7 @@ B220Processor.prototype.execute = function execute() {
if (!this.cardatron) {
this.setCardatronCheck(1);
} else {
this.selectedUnit = (this.CCONTROL >>> 12) & 0x0F;
this.selectedUnit = (this.CCONTROL >>> 12)%0x10;
d = this.cardatron.outputReadyInterrogate(this.selectedUnit);
if (d < 0) { // invalid unit
this.setCardatronCheck(1);
@ -3906,63 +3871,6 @@ B220Processor.prototype.execute = function execute() {
this.operationComplete();
break;
} // switch this.COP
/***************************************************************************
switch (-1) {
case 0x40: //---------------- MTR Magnetic Tape Read
if (!this.magTape) {
//this.schedule();
} else {
this.selectedUnit = (this.CCONTROL >>> 4) & 0x0F;
d = (this.CCONTROL >>> 8) & 0xFF; // number of blocks
this.togMT3P = 1;
this.togMT1BV4 = d%2; // select initial loop buffer
this.togMT1BV5 = 1-this.togMT1BV4;
this.execTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O
if (this.magTape.read(this.selectedUnit, d, this.boundMagTapeReceiveBlock)) {
this.OFT.set(1); // control or tape unit busy/not-ready
this.togMT3P = this.togMT1BV4 = this.togMT1BV5 = 0;
//this.schedule();
}
}
break;
case 0x42: //---------------- MTS Magnetic Tape Search
if (this.magTape) {
this.selectedUnit = (this.CCONTROL >>> 4) & 0x0F;
d = (this.CCONTROL >>> 8) & 0xFF; // lane number
if (this.magTape.search(this.selectedUnit, d, this.CADDR)) {
this.OFT.set(1); // control or tape unit busy/not-ready
}
}
//this.schedule();
break;
case 0x50: //---------------- MTW Magnetic Tape Write
if (!this.magTape) {
//this.schedule();
} else {
this.selectedUnit = (this.CCONTROL >>> 4) & 0x0F;
d = (this.CCONTROL >>> 8) & 0xFF; // number of blocks
this.togMT3P = 1;
this.togMT1BV4 = d%2; // select initial loop buffer
this.togMT1BV5 = 1-this.togMT1BV4;
this.execTime -= performance.now()*B220Processor.wordsPerMilli; // mark time during I/O
if (this.magTape.write(this.selectedUnit, d, this.boundMagTapeInitiateSend)) {
this.OFT.set(1); // control or tape unit busy/not-ready
this.togMT3P = this.togMT1BV4 = this.togMT1BV5 = 0;
//this.schedule();
}
}
break;
default: //---------------- (unimplemented instruction -- alarm)
break;
} // switch this.COP
***************************************************************************/
};
@ -4025,6 +3933,7 @@ B220Processor.prototype.ioInitiate = function ioInitiate() {
/* Initiates asynchronous mode of the processor for I/O */
this.AST.set(1);
this.updateGlow(1); // update the console lamps
this.execLimit = 0; // kill the run() loop
};
@ -4348,9 +4257,22 @@ B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() {
this.MM[ 2] = 0x1000540000; // MIW 0,1,10,100
this.MM[ 3] = 0x1750540100; // MIW 100,1,7,50
this.MM[ 4] = 0x1500550079; // MIR 79,1,5,00
this.MM[ 5] = 0x1101540200; // MIW 200,1,1,1
this.MM[ 6] = 0x1009500000; // MDA 1
this.MM[ 7] = 0x7777009999; // HLT 9999,7777
this.MM[ 5] = 0x1101542000; // MIW 2000,1,1,1 // write control block
this.MM[ 6] = 0x1008500000; // MRW 1
this.MM[ 7] = 0x1000560000; // MOW 0,1,10,100
this.MM[ 8] = 0x1750560100; // MOW 100,1,7,50
this.MM[ 9] = 0x1500570079; // MOR 79,1,5,00
this.MM[ 10] = 0x1101012000; // MOW 2000,1,1,1 // TEMP: changed to a NOP
this.MM[ 11] = 0x1008500000; // MRW 1
this.MM[ 12] = 0x1000523000; // MRD 3000,1,10,0
this.MM[ 13] = 0x1700524000; // MRD 4000,1,7,0
this.MM[ 14] = 0x1500534350; // MRR 4350,1,5,0
this.MM[ 15] = 0x1100534800; // MRR 4800,1,1,0 // should be a control block
this.MM[ 16] = 0x1009500000; // MDA 1
this.MM[ 17] = 0x7777009999; // HLT 9999,7777
this.MM[ 79] = 0x1900000000; // preface for 19 words, 80-98
this.MM[ 99] = 0x4000000000; // preface for 40 words, 100-139
@ -4358,6 +4280,10 @@ B220Processor.prototype.loadDefaultProgram = function loadDefaultProgram() {
this.MM[ 199] = 0x9900000000; // preface for 99 words, 200-298
this.MM[ 299] = 0x0000000000; // preface for 100 words, 300-399
this.MM[2000] = 0x9920012002; // end-of-tape control word
this.MM[2001] = 0x9999999999; // storage for end-of-tape block state
this.MM[2002] = 0x9999008421; // HLT: target for end-of-tape control branch
// Simple counter speed test
this.MM[ 80] = 0x0000120082; // ADD 82
this.MM[ 81] = 0x0000300080; // BUN 80

View File

@ -35,17 +35,9 @@ function B220MagTapeControl(p) {
this.boundControlFinished = B220Util.bindMethod(this, B220MagTapeControl.prototype.controlFinished);
this.boundTapeUnitFinished = B220Util.bindMethod(this, B220MagTapeControl.prototype.tapeUnitFinished);
this.boundReadReceiveBlock = B220Util.bindMethod(this, B220MagTapeControl.prototype.readReceiveBlock);
this.boundWriteTerminate = B220Util.bindMethod(this, B220MagTapeControl.prototype.writeTerminate);
this.boundWriteSendBlock = B220Util.bindMethod(this, B220MagTapeControl.prototype.writeSendBlock);
this.boundWriteInitiate = B220Util.bindMethod(this, B220MagTapeControl.prototype.writeInitiate);
this.boundSearchComplete = B220Util.bindMethod(this, B220MagTapeControl.prototype.searchComplete);
this.boundSwitch_Click = B220Util.bindMethod(this, B220MagTapeControl.prototype.switch_Click);
this.currentUnit = null; // stashed tape unit object
this.memoryBlockCallback = null; // stashed block-sending/receiving call-back function
this.memoryTerminateCallback = null;// stashed memory-sending terminate call-back function
this.tapeBlock = new Float64Array(101);
// block buffer for tape I/O
/* Set up the tape units from the system configuration. These can be any
combination of Tape Storage Units (DataReaders) and DataFiles. The indexes
@ -69,10 +61,10 @@ function B220MagTapeControl(p) {
u = this.config.getNode("MagTape.units", x);
switch (u.type.substring(0, 2)) {
case "MT":
this.tapeUnit[x] = new B220MagTapeDrive(u.type, x, this.config);
this.tapeUnit[x] = new B220MagTapeDrive(u.type, x, this, this.config);
break;
case "DF":
this.tapeUnit[x] = new B220DataFile(u.type, x, this.config);
this.tapeUnit[x] = new B220DataFile(u.type, x, this, this.config);
break;
default:
this.tapeUnit[x] = null;
@ -90,13 +82,11 @@ B220MagTapeControl.prototype.$$ = function $$(e) {
B220MagTapeControl.prototype.clear = function clear() {
/* Initializes (and if necessary, creates) the panel state */
this.MISC = 0; // Miscellaneous control register
this.C = 0; // C register (block number, etc.)
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.blockCount = 0; // number of blocks for current operation
this.blockWords = 0; // number of words/block for current operation
this.controlBusy = false; // control unit is busy with read/write/search
@ -144,6 +134,16 @@ B220MagTapeControl.prototype.queuePendingOperation = function queuePendingOperat
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, releaseProcessor, callee, args) {
/* If the control unit or the tape unit addressed by the unit field in dReg
@ -153,8 +153,8 @@ B220MagTapeControl.prototype.loadCommand = function loadCommand(dReg, releasePro
calls the releaseProcessor call-back 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, this.unitIndex, this.blockCount, and this.blockWords from
the digits in T. Then returns true */
Sets this.unitNr, this.unitIndex, and this.blockWords from the digits in T.
Sets this.currentUnit to the current tape unit object. Then returns true */
var c; // scratch
var t = dReg%0x10000000000; // scratch
var ux; // internal unit index
@ -164,31 +164,33 @@ B220MagTapeControl.prototype.loadCommand = function loadCommand(dReg, releasePro
if (this.controlBusy) {
this.queuePendingOperation(callee, args);
} else {
this.MISC = 0;
this.T = 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.blockWords = t%0x100;
this.blockCount = ((t - this.blockWords)/0x100)%0x10;
if (this.blockWords == 0) {
this.blockWords = 100;
} else {
if (this.blockWords > 0) {
this.blockWords = B220Processor.bcdBinary(this.blockWords);
} else {
this.blockWords = 100;
}
this.C = this.unitNr*0x100000 + t*0x10 + c;
this.regMisc.update(this.MISC);
this.clearMisc();
this.regC.update(this.C);
this.regT.update(this.T);
this.unitIndex = ux = this.findDesignate(this.unitNr);
if (ux < 0) {
this.reportStatus(2); // drive not ready, not present
setCallback(this.mnemonic, this, 0, releaseProcessor, true);
} else if (this.tapeUnit[ux].busy) {
this.queuePendingOperation(callee, args);
} else {
result = true;
this.currentUnit = this.tapeUnit[ux];
if (this.currentUnit.busy || this.currentUnit.rewindLock) {
this.queuePendingOperation(callee, args);
} else {
result = true;
}
}
}
@ -201,8 +203,6 @@ B220MagTapeControl.prototype.controlFinished = function controlFinished(alarm) {
back to simulate the amount of time the control unit is busy with an I/O.
If alarm is true, sets the Processor's Magnetic Tape Check alarm.
If another operation is pending, initiates that operation */
var args; // pending Arguments object
var callee; // pending method to call
//console.log(this.mnemonic + " controlFinished: " + alarm + ", busy=" + this.controlBusy);
if (alarm) {
@ -211,20 +211,19 @@ B220MagTapeControl.prototype.controlFinished = function controlFinished(alarm) {
this.controlBusy = false;
if (this.pendingCallee !== null) {
callee = this.pendingCallee;
args = this.pendingArgs;
this.pendingCallee = this.pendingArgs = null;
callee.apply(this, args);
this.dequeuePendingOperation();
}
};
/**************************************/
B220MagTapeControl.prototype.tapeUnitFinished = function tapeUnitFinished(alarm) {
B220MagTapeControl.prototype.tapeUnitFinished = function tapeUnitFinished() {
/* Call-back function passed to tape unit methods to signal when the unit has
completed its asynchronous operation */
if (!this.controlBusy) { // if the control unit is currently idle...
this.controlFinished(alarm); // initiate any pending operation
if (this.pendingCallee !== null) {
this.dequeuePendingOperation();
}
}
};
@ -252,10 +251,58 @@ B220MagTapeControl.prototype.decrementBlockCount = function decrementBlockCount(
};
/**************************************/
B220MagTapeControl.prototype.ClearBtn_onClick = function ClearBtn_onClick(ev) {
/* Handle the click event for the tape control CLEAR button */
B220MagTapeControl.prototype.clearMisc = function clearMisc() {
/* Resets this.regMisc and the individual lamps for that register */
var bitNr;
var m = this.regMisc;
this.clearUnit();
m.update(0);
for (bitNr=m.bits-1; bitNr>= 0; --bitNr) {
m.lamps[bitNr].set(0);
}
};
/**************************************/
B220MagTapeControl.prototype.reportStatus = function reportStatus(code) {
/* Sets bits in the MISC register to indicate various drive and control unit
status and error conditions */
switch (code) {
case 1: // report tape unit ready
this.TX2Lamp.set(0);
this.TX10Lamp.set(0);
break;
case 2: // report tape unit not ready
this.TX2Lamp.set(1);
this.TX10Lamp.set(1);
break;
case 4: // read check
this.TYC1Lamp.set(1);
this.TYC2Lamp.set(1);
break;
} // switch code
};
/**************************************/
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
};
/**************************************/
@ -304,276 +351,219 @@ B220MagTapeControl.prototype.magTapeOnLoad = function magTapeOnLoad() {
this.TX2Lamp = this.regMisc.lamps[0];
this.TX2Lamp.setCaption("TX2", true);
// C Register
this.regC = new PanelRegister(this.$$("CRegPanel"), 6*4, 4, "C_", "C");
// T Register
// 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);
this.$$("ClearBtn").addEventListener("click",
B220Util.bindMethod(this, B220MagTapeControl.prototype.ClearBtn_onClick));
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.readTerminate = function readTerminate() {
/* Terminates the read operation, sets the control to not-busy, and signals
the processor we are finished with the I/O */
B220MagTapeControl.prototype.search = function search(dReg, releaseProcessor, bReg, fetchWord) {
/* Searches a tape unit for a block with a keyWord matching the word at the
operand address in memory. "bReg is the contents of the B register for a
search, or 0 for a full-word search. This routine is used by MTS and MFS */
var alarm = false; // error result
var blocksLeft = true; // true => more blocks to process
var searchWord; // target word to search for
var that = this; // local self-reference
this.controlBusy = false;
this.currentUnit.readTerminate();
};
function signalControl(controlWord) {
/* Call-back function to send the EOT or control word to the Processor
and release it for the next operation */
/**************************************/
B220MagTapeControl.prototype.readReceiveBlock = function readReceiveBlock(block, abortRead) {
/* Receives the next block read by the tape unit. Sends the block to the
processor for storage in memory, updates the block counter, and if not
finished, requests the next block from the tape. Termination is a little
tricky here, as readTerminate() must be called to release the drive before
the block is stored in memory (and p.executeComplete() called to advance to
the next instruction, but if the memory call-back tells us the processor
has been cleared, we must release the drive after the attempt to store the
block in memory. Mess with the sequencing below at your peril */
var lastBlock;
var t = B220Processor.bcdBinary(this.T);
releaseProcessor(false, true, controlWord);
}
if (abortRead) {
this.readTerminate();
this.memoryBlockCallback(null, true);
} else {
// Decrement the block counter in the T register:
t = (t + 990)%1000; // subtract 1 from the counter field without overflow
this.T = B220Processor.binaryBCD(t);
this.regT.update(this.T);
function blockReady(alarm, control, controlWord, readBlock, completed) {
/* Call-back function when the drive is ready to send the next block
of data, or when it has encountered an error such as EOT. "alarm"
indicates that an error has occurred and the operation is to be aborted.
"control" inidicates that an EOT or control block was encountered, and
"controlWord" is to be passed to the Processor for handling. Otherwise,
if there are more blocks to write, fetches the next block from the
Processor and calls the drive's "readBlock" function, Finally calls
"completed" to finish the operation */
// If there are more blocks to read, request the next one
lastBlock = (t < 10);
if (lastBlock) {
this.readTerminate();
abortRead = this.memoryBlockCallback(block, true);
if (alarm) {
setCallback(that.mnemonic, that, 0, releaseProcessor, true);
completed(true); // drive detected an error
} else if (control) {
setCallback(that.mnemonic, that, 0, signalControl, controlWord);
completed(false);
} else if (blocksLeft) {
blocksLeft = that.decrementBlockCount(); // set to false on last block
readBlock(storeWord, record, controlEnabled);// read the next block
} else {
abortRead = this.memoryBlockCallback(block, false);
if (abortRead) { // processor was cleared
this.readTerminate();
} else { // at least one block left to go
this.currentUnit.readBlock(this.boundReadReceiveBlock);
}
setCallback(that.mnemonic, that, 0, releaseProcessor, false);
completed(false); // normal termination
}
}
if (this.loadCommand(dReg, releaseProcessor, search, arguments)) {
this.controlBusy = true;
searchWord = fetchWord(true);
if (searchWord < 0) {
alarm = true;
} else {
alarm = this.currentUnit.searchBlock(blockReady, this.boundControlFinished);
}
if (alarm) {
setCallback(this.mnemonic, this, 0, releaseProcessor, alarm);
this.controlFinished(true);
}
}
};
/**************************************/
B220MagTapeControl.prototype.read = function read(unitNr, blocks, blockSender) {
/* Initiates a read on the designated unit. "blocks" is the number of blocks
to read in BCD; "blockSender" is the call-back function to send a block of data
to the processor. "terminator" is the call-back function to tell the Processor
the I/O is finished. Returns true if the control is still busy with another command
or the unit is busy, and does not do the read */
var result = false; // return value
var unit; // tape unit object
var ux; // internal unit index
B220MagTapeControl.prototype.read = function read(dReg, releaseProcessor, record, storeWord) {
/* 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. "storeWord" is a function to store a word to the
Processor's memory. This routine is used by MRD and MRR */
var alarm = false; // error result
var blocksLeft = true; // true => more blocks to process
var controlEnabled = false; // true => control blocks will be recognized
var that = this; // local self-reference
if (this.controlBusy) {
result = true;
} else {
this.C = unitNr;
this.regC.update(unitNr);
ux = this.findDesignate(unitNr);
if (ux < 0) {
result = true;
function signalControl(controlWord) {
/* Call-back function to send the EOT or control word to the Processor
and release it for the next operation */
releaseProcessor(false, true, controlWord);
}
function blockReady(alarm, control, controlWord, readBlock, completed) {
/* Call-back function when the drive is ready to send the next block
of data, or when it has encountered an error such as EOT. "alarm"
indicates that an error has occurred and the operation is to be aborted.
"control" inidicates that an EOT or control block was encountered, and
"controlWord" is to be passed to the Processor for handling. Otherwise,
if there are more blocks to write, fetches the next block from the
Processor and calls the drive's "readBlock" function, Finally calls
"completed" to finish the operation */
if (alarm) {
setCallback(that.mnemonic, that, 0, releaseProcessor, true);
completed(true); // drive detected an error
} else if (control) {
setCallback(that.mnemonic, that, 0, signalControl, controlWord);
completed(false);
} else if (blocksLeft) {
blocksLeft = that.decrementBlockCount(); // set to false on last block
readBlock(storeWord, record, controlEnabled);// read the next block
} else {
this.controlBusy = true;
this.currentUnit = unit = this.tapeUnit[ux];
this.MISC = B220Processor.binaryBCD(unit.laneNr);
this.regMisc.update(this.MISC);
this.T = blocks*0x10 + unitNr;
this.regT.update(this.T);
this.memoryBlockCallback = blockSender;
result = unit.readInitiate(this.boundReadReceiveBlock);
if (result) {
this.controlBusy = false;
}
setCallback(that.mnemonic, that, 0, releaseProcessor, false);
completed(false); // normal termination
}
}
return result;
};
/**************************************/
B220MagTapeControl.prototype.writeTerminate = function writeTerminate(abortWrite) {
/* Called by the drive after the last block is written to release the
control unit and terminate the I/O. Note that "abortWrite" is not used, but
exists for signature compatibility with writeSendBlock() */
this.recordLamp.set(0);
this.controlBusy = false;
this.memoryTerminateCallback();
};
/**************************************/
B220MagTapeControl.prototype.writeSendBlock = function writeSendBlock(abortWrite) {
/* Called by the tape drive when it is ready for the next block to be written.
Retrieves the next buffered block from the Processor and passes it to the drive.
Unless this is the last block to write, the drive will call this again after
tape motion is complete. Note that this.memoryBlockCallback() will return true
if the processor has been cleared and the I/O must be aborted */
var aborted; // true if processor aborted the I/O
var lastBlock = abortWrite; // true if this will be the last block
var t = B220Processor.bcdBinary(this.T);
// First, decrement the block counter in the T register:
t = (t + 990)%1000; // subtract 1 from the counter field without overflow
this.T = B220Processor.binaryBCD(t);
this.regT.update(this.T);
if (t < 10) {
lastBlock = true;
}
aborted = this.memoryBlockCallback(this.tapeBlock, lastBlock);
if (abortWrite || aborted) {
this.writeTerminate(false);
} else if (lastBlock) {
this.currentUnit.writeBlock(this.tapeBlock, this.boundWriteTerminate, true);
} else {
this.currentUnit.writeBlock(this.tapeBlock, this.boundWriteSendBlock, false);
}
};
/**************************************/
B220MagTapeControl.prototype.writeInitiate = function writeInitiate(blockReceiver, terminator) {
/* Call-back function called by the Processor once the initial block to be
written is buffered in one of the high-speed loops. Once this block is
buffered, the drive can start tape motion and begin writing to tape */
this.memoryBlockCallback = blockReceiver;
this.memoryTerminateCallback = terminator;
this.currentUnit.writeInitiate(this.boundWriteSendBlock);
};
/**************************************/
B220MagTapeControl.prototype.write = function write(unitNr, blocks, receiveInitiate) {
/* Initiates a write on the designated unit. "blocks" is the number of blocks
to write in BCD; "receiveInitiate" is the call-back function to begin memory
transfer from the processor. Returns true if the control is still busy with
another command or the unit is busy, and does not do the write */
var result = false; // return value
var unit; // tape unit object
var ux; // internal unit index
if (this.controlBusy) {
result = true;
} else {
this.C = unitNr;
this.regC.update(unitNr);
ux = this.findDesignate(unitNr);
if (ux < 0) {
result = true;
} else {
this.controlBusy = true;
this.currentUnit = unit = this.tapeUnit[ux];
this.MISC = B220Processor.binaryBCD(unit.laneNr);
this.regMisc.update(this.MISC);
this.T = blocks*0x10 + unitNr;
this.regT.update(this.T);
result = unit.writeReadyTest();
if (result) {
this.controlBusy = false;
} else {
receiveInitiate(this.boundWriteInitiate);
}
if (this.loadCommand(dReg, releaseProcessor, read, arguments)) {
this.controlBusy = true;
controlEnabled = (this.blockWords%2 == 0); // low-order bit of v-digit
alarm = this.currentUnit.readBlock(blockReady, this.boundControlFinished);
if (alarm) {
setCallback(this.mnemonic, this, 0, releaseProcessor, alarm);
this.controlFinished(true);
}
}
return result;
};
/**************************************/
B220MagTapeControl.prototype.searchComplete = function searchComplete(success) {
/* Resets the busy status at the completion of a search */
var d; // scratch digit
if (success) {
// rotate T one digit right at end of successful search
d = this.T % 0x10;
this.T = d*0x1000 + (this.T - d)/0x10;
this.regT.update(this.T);
}
this.controlBusy = false;
};
/**************************************/
B220MagTapeControl.prototype.search = function search(unitNr, laneNr, addr) {
/* Initiates a search on the designated unit. "laneNr" is the lane number in
BCD; "addr" is the number of the block to search for in BCD. The search
Takes place in the control unit and drive independently of the processor.
Returns true if the control is still busy with another command or the unit
is busy, and does not do the search */
var block = B220Processor.bcdBinary(addr);
var lane = B220Processor.bcdBinary(laneNr);
var result = false; // return value
var unit; // tape unit object
var ux; // internal unit index
if (this.controlBusy) {
result = true;
} else {
this.C = unitNr;
this.regC.update(unitNr);
this.MISC = laneNr;
this.regMisc.update(laneNr);
this.T = addr;
this.regT.update(addr);
ux = this.findDesignate(unitNr);
if (ux < 0) {
result = true;
} else {
this.controlBusy = true;
unit = this.tapeUnit[ux];
result = unit.search(lane, block, this.boundSearchComplete,
this.boundDirectionLampSet, this.boundTestDisabled);
if (result) {
this.controlBusy = false;
}
}
}
return result;
};
/**************************************/
B220MagTapeControl.prototype.initialWrite = function initialWrite(dReg, releaseProcessor, fetchBlock) {
/* Initial-writes the number of blocks and of the size indicated in dReg.
This routine is used by MIW */
B220MagTapeControl.prototype.overwrite = function overwrite(dReg, releaseProcessor, record, fetchWord) {
/* 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. "fetchWord" is a function to
read a word from the Processor's memory. This routine is used by MOW and MOR */
var alarm = false; // error result
var blocksLeft = true; // true => more blocks to process
var that = this; // local self-reference
var unit = null; // selected tape unit
var words;
function signalControl(controlWord) {
/* Call-back function to send the EOT control word to the Processor
and release it for the next operation */
releaseProcessor(false, true, controlWord);
}
function blockReady(alarm, control, controlWord, writeBlock, completed) {
/* Call-back function when the drive is ready to receive the next block
of data, or when it has encountered an error such as EOT. "alarm"
indicates that an error has occurred and the operation is to be aborted.
"control" inidicates that an EOT block with a preface mismatch occurred,
and "controlWord" is to be passed to the Processor for handling.
Otherwise, if there are more blocks to write, fetches the next block
from the Processor and calls the drive's "writeBlock" function, Finally
calls "completed" to finish the operation */
if (alarm) {
setCallback(that.mnemonic, that, 0, releaseProcessor, true);
completed(true); // drive detected an error
} else if (control) {
setCallback(that.mnemonic, that, 0, signalControl, controlWord);
completed(false);
} else if (blocksLeft) {
blocksLeft = that.decrementBlockCount(); // set to false on last block
writeBlock(fetchWord, words); // write the next block
} else {
setCallback(that.mnemonic, that, 0, releaseProcessor, false);
completed(false); // normal termination
}
}
if (this.loadCommand(dReg, releaseProcessor, overwrite, arguments)) {
this.controlBusy = true;
if (this.blockWords < this.currentUnit.minBlockWords && this.blockWords > 1) {
alarm = true; // invalid block length
} else {
words = (record ? 0 : this.blockWords);
alarm = this.currentUnit.overwriteBlock(blockReady, this.boundControlFinished);
}
if (alarm) {
setCallback(this.mnemonic, this, 0, releaseProcessor, alarm);
this.controlFinished(true);
}
}
};
/**************************************/
B220MagTapeControl.prototype.initialWrite = function initialWrite(dReg, releaseProcessor, record, fetchWord) {
/* 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. fetchWord" is a function to
read a word from the Processor's memory. This routine is used by MIW and MIR */
var alarm = false; // error result
var blocksLeft = true; // true => more blocks to process
var that = this; // local self-reference
var words;
function blockReady(alarm, writeBlock, completed) {
/* Call-back function when the drive is ready to receive the next block
of data, or when it has encountered an error such as EOT. If there are
more blocks to write, fetches the next block from the Processor and calls
the drive's "writeBlock" function, otherwise calls "completed" to finish
the operation */
of data, or when it has encountered an error such as EOT. "alarm"
indicates that an error has occurred and the operation is to be aborted.
Otherwise, if there are more blocks to write, fetches the next block
from the Processor and calls the drive's "writeBlock" function. Finally
calls "completed" to finish the operation */
if (alarm) {
setCallback(that.mnemonic, that, 0, releaseProcessor, true);
completed(true); // drive detected an error
} else if (blocksLeft) {
blocksLeft = that.decrementBlockCount(); // set to false on last block
if (that.blockWords < unit.minBlockWords && that.blockWords > 1) {
completed(true); // invalid block size
} else if (fetchBlock(that.tapeBlock, that.blockWords)) {
completed(true); // Processor cleared or memory address error
} else {
writeBlock(that.tapeBlock, that.blockWords); // write next block
}
writeBlock(fetchWord, words); // write the next block
} else {
setCallback(that.mnemonic, that, 0, releaseProcessor, false);
completed(false); // normal termination
@ -582,70 +572,16 @@ B220MagTapeControl.prototype.initialWrite = function initialWrite(dReg, releaseP
if (this.loadCommand(dReg, releaseProcessor, initialWrite, arguments)) {
this.controlBusy = true;
unit = this.tapeUnit[this.unitIndex];
alarm = unit.initialWriteBlock(blockReady, this.boundControlFinished);
if (alarm) {
setCallback(this.mnemonic, this, 0, releaseProcessor, alarm);
controlFinished(true);
}
}
};
/**************************************/
B220MagTapeControl.prototype.initialWriteRecord = function initialWriteRecord(dReg, releaseProcessor, fetchBlock) {
/* Initial-writes the number of blocks indicated in dReg. Block lengths
(preface words) are taken from the word in memory preceding the data to be
written. This routine is used by MIR */
var alarm = false; // error result
var blocksLeft = true; // true => more blocks to process
var that = this; // local self-reference
var unit = null; // selected tape unit
function blockReady(alarm, writeBlock, completed) {
/* Call-back function when the drive is ready to receive the next block
of data, or when it has encountered an error such as EOT. If there are
more blocks to write, fetches the next block and its size from the
Processor and calls the drive's "writeBlock" function, otherwise calls
"completed" to finish the operation */
var words; // words to write for block
if (alarm) {
setCallback(that.mnemonic, that, 0, releaseProcessor, true);
completed(true); // drive detected an error
} else if (blocksLeft) {
blocksLeft = that.decrementBlockCount(); // set to false on last block
if (fetchBlock(that.tapeBlock, 1)) { // fetch the preface word
completed(true); // Processor cleared or memory address error
} else {
words = that.tapeBlock[0]; // convert preface to binary
words = ((words - words%0x100000000)/0x100000000)%0x100;
if (words) {
words = (words >>> 4)*10 + words%0x10;
} else {
words = 100; // preface == 0 => 100
}
if (words < unit.minBlockWords && words > 1) {
completed(true); // invalid block size
} else if (fetchBlock(that.tapeBlock, words)) {
completed(true); // Processor cleared or memory address error
} else {
writeBlock(that.tapeBlock, words); // write next block
}
}
if (this.blockWords < this.currentUnit.minBlockWords && this.blockWords > 1) {
alarm = true; // invalid block length
} else {
setCallback(that.mnemonic, that, 0, releaseProcessor, false);
completed(false); // normal termination
words = (record ? 0 : this.blockWords);
alarm = this.currentUnit.initialWriteBlock(blockReady, this.boundControlFinished);
}
}
if (this.loadCommand(dReg, releaseProcessor, initialWriteRecord, arguments)) {
this.controlBusy = true;
unit = this.tapeUnit[this.unitIndex];
alarm = unit.initialWriteBlock(blockReady, this.boundControlFinished);
if (alarm) {
setCallback(this.mnemonic, this, 0, releaseProcessor, alarm);
controlFinished(true);
this.controlFinished(true);
}
}
};
@ -655,7 +591,6 @@ B220MagTapeControl.prototype.positionForward = function positionForward(dReg, re
/* Positions the tape forward the number of blocks indicated in dReg */
var alarm = false; // error result
var that = this; // local self-reference
var unit = null; // selected tape unit
function blockFinished(nextBlock, completed) {
/* Call-back function when the drive has finished spacing one block
@ -671,8 +606,7 @@ B220MagTapeControl.prototype.positionForward = function positionForward(dReg, re
if (this.loadCommand(dReg, releaseProcessor, positionForward, arguments)) {
this.controlBusy = true;
unit = this.tapeUnit[this.unitIndex];
alarm = unit.positionForward(blockFinished, this.boundControlFinished);
alarm = this.currentUnit.positionForward(blockFinished, this.boundControlFinished);
setCallback(this.mnemonic, this, 0, releaseProcessor, alarm);
}
};
@ -682,7 +616,6 @@ B220MagTapeControl.prototype.positionBackward = function positionBackward(dReg,
/* Positions the tape backward the number of blocks indicated in dReg */
var alarm = false; // error result
var that = this; // local self-reference
var unit = null; // selected tape unit
function blockFinished(nextBlock, completed) {
/* Call-back function when the drive has finished spacing one block
@ -698,8 +631,7 @@ B220MagTapeControl.prototype.positionBackward = function positionBackward(dReg,
if (this.loadCommand(dReg, releaseProcessor, positionBackward, arguments)) {
this.controlBusy = true;
unit = this.tapeUnit[this.unitIndex];
alarm = unit.positionBackward(blockFinished, this.boundControlFinished);
alarm = this.currentUnit.positionBackward(blockFinished, this.boundControlFinished);
setCallback(this.mnemonic, this, 0, releaseProcessor, alarm);
}
};
@ -713,13 +645,13 @@ B220MagTapeControl.prototype.positionAtEnd = function positionAtEnd(dReg, releas
if (this.loadCommand(dReg, releaseProcessor, positionAtEnd, arguments)) {
this.controlBusy = true;
alarm = this.tapeUnit[this.unitIndex].positionAtEnd(this.boundControlFinished);
alarm = this.currentUnit.positionAtEnd(this.boundControlFinished);
setCallback(this.mnemonic, this, 0, releaseProcessor, alarm);
}
};
/**************************************/
B220MagTapeControl.prototype.laneSelect = function laneSelect(dReg, releaseProcessor) {
B220MagTapeControl.prototype.laneSelect = function laneSelect(dReg, releaseProcessor, fetchWord) {
/* Selects the tape lane of the designated unit. Returns an alarm if the
unit does not exist or is not ready */
var alarm = false; // error result
@ -728,13 +660,14 @@ B220MagTapeControl.prototype.laneSelect = function laneSelect(dReg, releaseProce
if (this.loadCommand(dReg, releaseProcessor,laneSelect, arguments)) {
this.controlBusy = true;
laneNr = ((this.C - this.C%0x100)/0x100)%2;
alarm = this.tapeUnit[this.unitIndex].laneSelect(laneNr, this.boundControlFinished);
fetchWord(true); // memory access for MTS/MFS not used by MLS
alarm = this.currentUnit.laneSelect(laneNr, this.boundControlFinished);
setCallback(this.mnemonic, this, 0, releaseProcessor, alarm);
}
};
/**************************************/
B220MagTapeControl.prototype.rewind = function rewind(dReg, releaseProcessor) {
B220MagTapeControl.prototype.rewind = function rewind(dReg, releaseProcessor, fetchWord) {
/* Initiates rewind of the designated unit. Returns an alarm if the unit
does not exist or is not ready */
var alarm = false; // error result
@ -745,7 +678,8 @@ B220MagTapeControl.prototype.rewind = function rewind(dReg, releaseProcessor) {
this.controlBusy = true;
laneNr = ((this.C - this.C%0x100)/0x100)%2;
lockout = ((this.C - this.C%0x10)/0x10)%2;
alarm = this.tapeUnit[this.unitIndex].rewind(laneNr, lockout, this.boundTapeUnitFinished);
fetchWord(true); // memory access for MTS/MFS not used by MRW/MDA
alarm = this.currentUnit.rewind(laneNr, lockout);
setCallback(this.mnemonic, this, 50, this.controlFinished, false);
setCallback(this.mnemonic, this, 0, releaseProcessor, alarm);
}
@ -795,7 +729,7 @@ B220MagTapeControl.prototype.clearUnit = function clearUnit() {
/* Clears the internal state of the control unit */
this.clear();
this.regMisc.update(this.MISC);
this.clearMisc();
this.regC.update(this.C);
this.regT.update(this.T);
};
@ -814,6 +748,10 @@ B220MagTapeControl.prototype.shutDown = function shutDown() {
}
}
this.window.removeEventListener("beforeunload", B220MagTapeControl.prototype.beforeUnload);
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();
};

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
CACHE MANIFEST
# retro-220 emulator 0.03a, 2017-10-19 08:45
# retro-220 emulator 0.03b, 2017-11-02 12:30
CACHE:
../emulator/B220Processor.js
B220.css