diff --git a/emulator/B5500CentralControl.js b/emulator/B5500CentralControl.js index bec7052..adfe1ef 100644 --- a/emulator/B5500CentralControl.js +++ b/emulator/B5500CentralControl.js @@ -61,7 +61,7 @@ function B5500CentralControl(global) { /**************************************/ /* Global constants */ -B5500CentralControl.version = "0.18"; +B5500CentralControl.version = "0.19"; B5500CentralControl.memReadCycles = 2; // assume 2 µs memory read cycle time (the other option was 3 µs) B5500CentralControl.memWriteCycles = 4; // assume 4 µs memory write cycle time (the other option was 6 µs) diff --git a/emulator/B5500IOUnit.js b/emulator/B5500IOUnit.js index 84ca258..ac9bbb3 100644 --- a/emulator/B5500IOUnit.js +++ b/emulator/B5500IOUnit.js @@ -57,7 +57,7 @@ function B5500IOUnit(ioUnitID, cc) { }; // Establish a buffer for the peripheral modules to use during their I/O. - // The size is sufficient for 63 disk sectors, rounded up to the next 8KB. + // The 16384 size is sufficient for 63 disk sectors, rounded up to the next 8KB. this.bufferArea = new ArrayBuffer(16384); this.buffer = new Uint8Array(this.bufferArea); @@ -772,7 +772,7 @@ B5500IOUnit.prototype.finishGenericRead = function finishGenericRead(errorMask, /* Handles a generic I/O finish when input data transfer, and optionally, word-count update, is needed. Note that this turns off the busyUnit mask bit in CC */ - this.storeBuffer(length, 0, 1, (this.D23F ? this.DwordCount : this.buffer.length)); + this.storeBuffer(length, 0, 1, (this.D23F ? this.DwordCount : this.buffer.length/8)); this.finishGeneric(errorMask, length); }; @@ -957,7 +957,7 @@ B5500IOUnit.prototype.initiatePrinterIO = function initiatePrinterIO(u) { B5500IOUnit.prototype.finishSPORead = function finishSPORead(errorMask, length) { /* Handles I/O finish for a SPO keyboard input operation */ - this.storeBufferWithGM(length, 0, 1, this.buffer.length); + this.storeBufferWithGM(length, 0, 1, this.buffer.length/8); this.finishGeneric(errorMask, length); }; @@ -1019,18 +1019,6 @@ B5500IOUnit.prototype.finishTapeRead = function finishTapeRead(errorMask, length this.finishTapeIO(errorMask, count); }; -/**************************************/ -B5500IOUnit.prototype.finishTapeWrite = function finishTapeWrite(errorMask, length) { - /* Handles I/O finish for a tape drive write operation */ - - /*** Temporary stub until tape write is implemented ***/ - - this.finishTapeIO(errorMask, length); - - //console.log(this.mnemonic + " finishTapeWr: " + errorMask.toString(8) + " for " + length.toString() + - // ", D=" + this.D.toString(8)); -}; - /**************************************/ B5500IOUnit.prototype.initiateTapeIO = function initiateTapeIO(u) { /* Initiates an I/O to a Magnetic Tape unit */ @@ -1039,28 +1027,36 @@ B5500IOUnit.prototype.initiateTapeIO = function initiateTapeIO(u) { var memWords; // words to fetch from memory if (this.D24F) { // tape read operation - if (this.D18F) { // memory inhibit -- maintenance commands + if (this.D18F) { // read memory inhibit => maintenance commands this.D30F = 1; // (MAINTENANCE I/Os NOT YET IMPLEMENTED) this.finish(); } else if (this.D23F && this.DwordCount == 0) { // forward or backward space u.space(this.boundFinishTapeIO, 0, this.D22F); } else { // some sort of actual read - memWords = (this.D23F ? this.DwordCount : this.buffer.length); + memWords = (this.D23F ? this.DwordCount : this.buffer.length/8); u.read(this.boundFinishTapeRead, this.buffer, memWords*8, this.D21F, this.D22F); } } else { // tape write operation if (this.D23F && this.DwordCount == 0) {// interrogate drive status u.writeInterrogate(this.boundFinishTapeIO, 0); - } else if (this.D18F) { // memory inhibit - if (this.D22F) { // backward write => rewind - u.rewind(this.boundFinishTapeIO); - } else { - this.D30F = 1; // (ERASE NOT YET IMPLEMENTED) - this.finish(); + } else if (this.D22F) { // backward write => rewind + u.rewind(this.boundFinishTapeIO); + } else { // some sort of actual write + memWords = (this.D23F ? this.DwordCount : this.buffer.length/8); + if (!this.D21F) { // if alpha mode, fetch the characters + chars = this.fetchBufferWithGM(1, memWords); + } else { // otherwise, it's binary mode + if (this.D18F) { // if mem inhibit, just compute the size + chars = this.DwordCount*8; + } else { // otherwise, fetch the characters + chars = this.fetchBuffer(1, memWords); + } + } + if (this.D18F) { // write memory inhibit => erase + u.erase(this.boundFinishTapeIO, chars); + } else { // write data + u.write(this.boundFinishTapeIO, this.buffer, chars, this.D21F, 0); } - } else { - this.D30F = 1; // (ALL FORMS OF WRITE NOT YET IMPLEMENTED) - this.finish(); } } }; @@ -1143,9 +1139,9 @@ B5500IOUnit.prototype.forkIO = function forkIO() { // SPO designate case 30: if (this.D24F) { - u.read(this.boundFinishSPORead, this.buffer, this.buffer.length, 0, 0); + u.read(this.boundFinishSPORead, this.buffer, this.buffer.length/8, 0, 0); } else { - chars = this.fetchBufferWithGM(1, this.buffer.length); + chars = this.fetchBufferWithGM(1, this.buffer.length/8); u.write(this.boundFinishGeneric, this.buffer, chars, 0, 0); } break; diff --git a/tools/BCD-Build-Xlate.html b/tools/BCD-Build-Xlate.html index 2e8119a..1d972e8 100644 --- a/tools/BCD-Build-Xlate.html +++ b/tools/BCD-Build-Xlate.html @@ -42,6 +42,7 @@ BCLtoANSI = [ // Index by 6-bit BCL to get 8-bit ANSI code "H", "I", "+", ".", "[", "(", "<", "~"]; // 38-3F, @70-77 function html(c) { + /* Filter ANSI characters to escaped HTML */ switch(c) { case "<": return "<"; case ">": return ">"; @@ -53,12 +54,14 @@ function html(c) { } function parity(c) { + /* Compute even parity over six bits. Returns 0 or 1 */ return ((c & 0x01) ^ ((c & 0x02) >>> 1) ^ ((c & 0x04) >>> 2) ^ ((c & 0x08) >>> 3) ^ ((c & 0x10) >>> 4) ^ ((c & 0x20) >>> 5)); } function pic9n(v, n) { + /* Formats a value padded with leading zeroes to length "n" */ var text = v.toString(); if (text.length > n) { @@ -73,6 +76,8 @@ function pic9n(v, n) { var bcdEven = new Array(128); var bcdOdd = new Array(128); +var ansiOdd = new Array(128); +var ansiEven= new Array(128); var c; var even; @@ -84,7 +89,7 @@ document.write("
");
+document.write("BCD Odd Parity BIC to ANSI Table
");
for (x=0; x<128; x++) {
- document.write("0x" + bcdOdd[x].toString(16).toUpperCase() + ", ");
+ document.write("0x" + pic9n(bcdOdd[x].toString(16).toUpperCase(), 2) + ", ");
if (x % 16 == 15) {
document.write("
");
}
}
document.write("");
-document.write("Even Parity Table
");
+document.write("BCD Odd Parity ANSI to BIC Table
");
for (x=0; x<128; x++) {
- document.write("0x" + bcdEven[x].toString(16).toUpperCase() + ", ");
+ document.write("0x" + pic9n(ansiOdd[x].toString(16).toUpperCase(), 2) + ", ");
+ if (x % 16 == 15) {
+ document.write("
");
+ }
+}
+document.write("");
+
+document.write("BCD Even Parity BCL to ANSI Table
");
+
+for (x=0; x<128; x++) {
+ document.write("0x" + pic9n(bcdEven[x].toString(16).toUpperCase(), 2) + ", ");
+ if (x % 16 == 15) {
+ document.write("
");
+ }
+}
+document.write("");
+
+document.write("BCD Even Parity ANSI to BCL Table
");
+
+for (x=0; x<128; x++) {
+ document.write("0x" + pic9n(ansiEven[x].toString(16).toUpperCase(), 2) + ", ");
if (x % 16 == 15) {
document.write("
");
}
diff --git a/webUI/B5500MagTapeDrive.css b/webUI/B5500MagTapeDrive.css
index 97534b3..039cbb0 100644
--- a/webUI/B5500MagTapeDrive.css
+++ b/webUI/B5500MagTapeDrive.css
@@ -24,7 +24,7 @@ DIV#MTDiv {
border-radius: 8px;
padding: 0;
vertical-align: top}
-
+
BUTTON.yellowButton {
background-color: #990;
color: black;
@@ -70,7 +70,7 @@ SPAN.annunciator {
font-family: Arial Rounded, Arial, Helvetica, sans-serif;
font-size: 7pt;
font-weight: bold}
-
+
SPAN.whiteLit {
color: white}
@@ -103,7 +103,7 @@ SPAN.whiteLit {
position: absolute;
top: 8px;
left: 348px;}
-
+
IMG#MTReel {
position: absolute;
height: 40px;
@@ -111,7 +111,7 @@ IMG#MTReel {
border-radius: 50%;
top: 8px;
left: 420px}
-
+
#MTUnloadedLight {
top: 4px;
right: 8px}
@@ -128,12 +128,13 @@ IMG#MTReel {
top: 34px;
right: 8px}
-#MTFileSelector {
+#MTFileName {
position: absolute;
- border: 1px solid white;
+ border: none;
+ background-color: #666;
color: white;
width: 530px;
- top: 54px;
+ top: 58px;
left: 8px}
#MTProgressBar {
@@ -142,3 +143,61 @@ IMG#MTReel {
width: 530px;
top: 84px;
left: 8px}
+
+/* Tape Load Panel */
+
+DIV#MTLoaderDiv {
+ background-color: #666;
+ width: 480px;
+ height: 86px;
+ border-radius: 8px;
+ padding: 8px;
+ color: white;
+ font-family: Arial Rounded, Arial, Helvetica, sans-serif;
+ font-size: 8pt;
+ font-weight: bold}
+
+DIV#MTLoaderInnerDiv {
+ position: relative;
+ height: 100%;
+ width: 100%}
+
+#MTLoadFileSelector {
+ position: absolute;
+ border: 1px solid white;
+ color: white;
+ width: 100%;
+ top: 0;
+ left: 0}
+
+#MTLoadFormatGroup {
+ position: absolute;
+ top: 32px;
+ left: 0}
+
+#MTLoadFormatSelect {
+ border: 1px solid white}
+
+#MTLoadWriteRingCheck {
+ border: 1px solid white}
+
+#MTLoadTapeLengthGroup {
+ position: absolute;
+ top: 32px;
+ right: 0}
+
+#MTLoadTapeLengthSelect {
+ border: 1px solid white}
+
+#MTLoadTapeLengthSelect OPTION {
+ text-align: right}
+
+#MTLoadCancelBtn {
+ position: absolute;
+ bottom: 0;
+ right: 64px}
+
+#MTLoadOKBtn {
+ position: absolute;
+ bottom: 0;
+ right: 0}
diff --git a/webUI/B5500MagTapeDrive.html b/webUI/B5500MagTapeDrive.html
index 9b91579..368850d 100644
--- a/webUI/B5500MagTapeDrive.html
+++ b/webUI/B5500MagTapeDrive.html
@@ -26,7 +26,7 @@
AT EOT
REWINDING
-
+
diff --git a/webUI/B5500MagTapeDrive.js b/webUI/B5500MagTapeDrive.js
index 7ec9875..17fd9f8 100644
--- a/webUI/B5500MagTapeDrive.js
+++ b/webUI/B5500MagTapeDrive.js
@@ -9,11 +9,21 @@
*
* Defines a magnetic tape drive peripheral unit type, emulating the
* Burroughs B425 tape transport at 800 bits/inch.
-* This implementation supports READONLY operation for .bcd files ONLY.
+*
+* Internally, tape images are maintained in ".bcd" format. Each character
+* frame on the tape is represented by one 8-bit byte in memory. The low-
+* order six bits of each frame are the character bits (in BCL if even
+* parity or BIC if odd parity). The seventh bit is even or odd parity,
+* and the high-order bit in the byte is one if this frame starts a block.
+* EOF is represented as a one-frame block with a code of 0x8F in both
+* odd- and even-parity recording.
*
************************************************************************
* 2013-10-26 P.Kimpel
* Original version, from B5500CardReader.js.
+* 2013-01-01 P.Kimpel
+* Add write capabilty, read capability for ASCII tape images, and
+* selectable tape lengths.
***********************************************************************/
"use strict";
@@ -65,44 +75,59 @@ B5500MagTapeDrive.prototype.startStopTime = 0.0045 + 0.0042;
// tape start+stop time [sec]
B5500MagTapeDrive.prototype.rewindSpeed = 320;
// rewind speed [inches/sec]
-B5500MagTapeDrive.prototype.maxTapeLength = 2400*12;
+B5500MagTapeDrive.prototype.maxTapeLength = 2410*12;
// max tape length on reel [inches]
+B5500MagTapeDrive.prototype.postEOTLength = 20*12;
+ // length of tape after EOT reflector [inches]
B5500MagTapeDrive.prototype.maxBlankFrames = 9*12*B5500MagTapeDrive.prototype.density;
// max blank tape length, 9 feet [frames]
B5500MagTapeDrive.prototype.bcdTapeMark = 0x8F;
// .bcd image EOF code
B5500MagTapeDrive.prototype.reelCircumference = 10*3.14159;
// max circumference of tape [inches]
+B5500MagTapeDrive.prototype.maxSpinAngle = 37;
+ // max angle to rotate reel image [degrees]
-B5500MagTapeDrive.prototype.cardFilter = [ // Filter ASCII character values to valid BIC ones
- 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 00-0F
- 0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F, // 10-1F
- 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x3F,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F, // 20-2F
- 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F, // 30-3F
- 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, // 40-4F
- 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x3F,0x5D,0x3F,0x3F, // 50-5F
- 0x3F,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F, // 60-6F
- 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x7B,0x7C,0x7D,0x7E,0x3F]; // 70-7F
+B5500MagTapeDrive.prototype.bcdXlateInOdd = [ // Translate odd parity BIC to ASCII
+ 0xFF,0x31,0x32,0xFF,0x34,0xFF,0xFF,0x37,0x38,0xFF,0xFF,0x40,0xFF,0x3A,0x3E,0xFF, // 00-0F
+ 0x2B,0xFF,0xFF,0x43,0xFF,0x45,0x46,0xFF,0xFF,0x49,0x2E,0xFF,0x26,0xFF,0xFF,0x7E, // 10-1F
+ 0x7C,0xFF,0xFF,0x4C,0xFF,0x4E,0x4F,0xFF,0xFF,0x52,0x24,0xFF,0x2D,0xFF,0xFF,0x7B, // 20-2F
+ 0xFF,0x2F,0x53,0xFF,0x55,0xFF,0xFF,0x58,0x59,0xFF,0xFF,0x25,0xFF,0x3D,0x5D,0xFF, // 30-3F
+ 0x30,0xFF,0xFF,0x33,0xFF,0x35,0x36,0xFF,0xFF,0x39,0x23,0xFF,0x3F,0xFF,0xFF,0x7D, // 40-4F
+ 0xFF,0x41,0x42,0xFF,0x44,0xFF,0xFF,0x47,0x48,0xFF,0xFF,0x5B,0xFF,0x28,0x3C,0xFF, // 50-5F
+ 0xFF,0x4A,0x4B,0xFF,0x4D,0xFF,0xFF,0x50,0x51,0xFF,0xFF,0x2A,0xFF,0x29,0x3B,0xFF, // 60-6F
+ 0x20,0xFF,0xFF,0x54,0xFF,0x56,0x57,0xFF,0xFF,0x5A,0x2C,0xFF,0x21,0xFF,0xFF,0x22]; // 70-7F
-B5500MagTapeDrive.prototype.bcdXlateInOdd = [ // Translate odd parity BIC to ASCII
- 0xFF,0x31,0x32,0xFF,0x34,0xFF,0xFF,0x37,0x38,0xFF,0xFF,0x40,0xFF,0x3A,0x3E,0xFF,
- 0x2B,0xFF,0xFF,0x43,0xFF,0x45,0x46,0xFF,0xFF,0x49,0x2E,0xFF,0x26,0xFF,0xFF,0x7E,
- 0x7C,0xFF,0xFF,0x4C,0xFF,0x4E,0x4F,0xFF,0xFF,0x52,0x24,0xFF,0x2D,0xFF,0xFF,0x7B,
- 0xFF,0x2F,0x53,0xFF,0x55,0xFF,0xFF,0x58,0x59,0xFF,0xFF,0x25,0xFF,0x3D,0x5D,0xFF,
- 0x30,0xFF,0xFF,0x33,0xFF,0x35,0x36,0xFF,0xFF,0x39,0x23,0xFF,0x3F,0xFF,0xFF,0x7D,
- 0xFF,0x41,0x42,0xFF,0x44,0xFF,0xFF,0x47,0x48,0xFF,0xFF,0x5B,0xFF,0x28,0x3C,0xFF,
- 0xFF,0x4A,0x4B,0xFF,0x4D,0xFF,0xFF,0x50,0x51,0xFF,0xFF,0x2A,0xFF,0x29,0x3B,0xFF,
- 0x20,0xFF,0xFF,0x54,0xFF,0x56,0x57,0xFF,0xFF,0x5A,0x2C,0xFF,0x21,0xFF,0xFF,0x22]
+B5500MagTapeDrive.prototype.bcdXlateOutOdd = [ // Translate ASCII to odd Parity BIC
+ 0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C, // 00-0F
+ 0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C,0x4C, // 10-1F
+ 0x70,0x7C,0x7F,0x4A,0x2A,0x3B,0x1C,0x4C,0x5D,0x6D,0x6B,0x10,0x7A,0x2C,0x1A,0x31, // 20-2F
+ 0x40,0x01,0x02,0x43,0x04,0x45,0x46,0x07,0x08,0x49,0x0D,0x6E,0x5E,0x3D,0x0E,0x4C, // 30-3F
+ 0x0B,0x51,0x52,0x13,0x54,0x15,0x16,0x57,0x58,0x19,0x61,0x62,0x23,0x64,0x25,0x26, // 40-4F
+ 0x67,0x68,0x29,0x32,0x73,0x34,0x75,0x76,0x37,0x38,0x79,0x5B,0x4C,0x3E,0x4C,0x4C, // 50-5F
+ 0x4C,0x51,0x52,0x13,0x54,0x15,0x16,0x57,0x58,0x19,0x61,0x62,0x23,0x64,0x25,0x26, // 60-6F
+ 0x67,0x68,0x29,0x32,0x73,0x34,0x75,0x76,0x37,0x38,0x79,0x2F,0x20,0x4F,0x1F,0x4C]; // 70-7F
+
+B5500MagTapeDrive.prototype.bcdXlateInEven = [ // Translate even parity BCL to ASCII
+ 0x3F,0xFF,0xFF,0x33,0xFF,0x35,0x36,0xFF,0xFF,0x39,0x30,0xFF,0x40,0xFF,0xFF,0x7D, // 00-0F
+ 0xFF,0x2F,0x53,0xFF,0x55,0xFF,0xFF,0x58,0x59,0xFF,0xFF,0x2C,0xFF,0x3D,0x5D,0xFF, // 10-1F
+ 0xFF,0x4A,0x4B,0xFF,0x4D,0xFF,0xFF,0x50,0x51,0xFF,0xFF,0x24,0xFF,0x29,0x3B,0xFF, // 20-2F
+ 0x26,0xFF,0xFF,0x43,0xFF,0x45,0x46,0xFF,0xFF,0x49,0x2B,0xFF,0x5B,0xFF,0xFF,0x7E, // 30-3F
+ 0xFF,0x31,0x32,0xFF,0x34,0xFF,0xFF,0x37,0x38,0xFF,0xFF,0x23,0xFF,0x3A,0x3E,0xFF, // 40-4F
+ 0x20,0xFF,0xFF,0x54,0xFF,0x56,0x57,0xFF,0xFF,0x5A,0x21,0xFF,0x25,0xFF,0xFF,0x22, // 50-5F
+ 0x2D,0xFF,0xFF,0x4C,0xFF,0x4E,0x4F,0xFF,0xFF,0x52,0x7C,0xFF,0x2A,0xFF,0xFF,0x7B, // 60-6F
+ 0xFF,0x41,0x42,0xFF,0x44,0xFF,0xFF,0x47,0x48,0xFF,0xFF,0x2E,0xFF,0x28,0x3C,0xFF]; // 70-7F
+
+B5500MagTapeDrive.prototype.bcdXlateOutEven = [ // Translate ASCII to even parity BCL
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 00-0F
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 10-1F
+ 0x50,0x5A,0x5F,0x4B,0x2B,0x5C,0x30,0x00,0x7D,0x2D,0x6C,0x3A,0x1B,0x60,0x7B,0x11, // 20-2F
+ 0x0A,0x41,0x42,0x03,0x44,0x05,0x06,0x47,0x48,0x09,0x4D,0x2E,0x7E,0x1D,0x4E,0x00, // 30-3F
+ 0x0C,0x71,0x72,0x33,0x74,0x35,0x36,0x77,0x78,0x39,0x21,0x22,0x63,0x24,0x65,0x66, // 40-4F
+ 0x27,0x28,0x69,0x12,0x53,0x14,0x55,0x56,0x17,0x18,0x59,0x3C,0x00,0x1E,0x00,0x00, // 50-5F
+ 0x00,0x71,0x72,0x33,0x74,0x35,0x36,0x77,0x78,0x39,0x21,0x22,0x63,0x24,0x65,0x66, // 60-6F
+ 0x27,0x28,0x69,0x12,0x53,0x14,0x55,0x56,0x17,0x18,0x59,0x6F,0x6A,0x0F,0x3F,0x00]; // 70-7F
-B5500MagTapeDrive.prototype.bcdXlateInEven = [ // Translate even parity BCL to ASCII
- 0x30,0xFF,0xFF,0x33,0xFF,0x35,0x36,0xFF,0xFF,0x39,0x23,0xFF,0x3F,0xFF,0xFF,0x7D, // 00-0F
- 0xFF,0x41,0x42,0xFF,0x44,0xFF,0xFF,0x47,0x48,0xFF,0xFF,0x5B,0xFF,0x28,0x3C,0xFF, // 10-1F
- 0xFF,0x4A,0x4B,0xFF,0x4D,0xFF,0xFF,0x50,0x51,0xFF,0xFF,0x2A,0xFF,0x29,0x3B,0xFF, // 20-2F
- 0x20,0xFF,0xFF,0x54,0xFF,0x56,0x57,0xFF,0xFF,0x5A,0x2C,0xFF,0x21,0xFF,0xFF,0x22, // 30-3F
- 0xFF,0x31,0x32,0xFF,0x34,0xFF,0xFF,0x37,0x38,0xFF,0xFF,0x40,0xFF,0x3A,0x3E,0xFF, // 40-4F
- 0x2B,0xFF,0xFF,0x43,0xFF,0x45,0x46,0xFF,0xFF,0x49,0x2E,0xFF,0x26,0xFF,0xFF,0x7E, // 50-5F
- 0x7C,0xFF,0xFF,0x4C,0xFF,0x4E,0x4F,0xFF,0xFF,0x52,0x24,0xFF,0x2D,0xFF,0xFF,0x7B, // 60-6F
- 0xFF,0x2F,0x53,0xFF,0x55,0xFF,0xFF,0x58,0x59,0xFF,0xFF,0x25,0xFF,0x3D,0x5D,0xFF]; // 70-7F
/**************************************/
B5500MagTapeDrive.prototype.$$ = function $$(e) {
@@ -119,13 +144,17 @@ B5500MagTapeDrive.prototype.clear = function clear() {
this.errorMask = 0; // error mask for finish()
this.finish = null; // external function to call for I/O completion
- this.image = null; // Tape drive "reel of tape"
- this.imgLength = 0; // Current input buffer length (characters)
+ this.image = null; // tape drive "reel of tape"
+ this.imgEOTInches = 0; // tape image length to EOT marker [inches]
this.imgIndex = 0; // 0-relative offset to next tape block to be read
+ this.imgLength = 0; // current input buffer length (characters)
+ this.imgMaxInches = 0; // tape image max length [inches]
+ this.imgTopIndex = 0; // highest-used offset within image data
+ this.imgWritten = false; // tape image has been modified (implies writable)
this.tapeState = this.tapeUnloaded; // tape drive state
this.angle = 0; // current rotation angle of reel image [degrees]
- this.tapeInches = 0; // number of inches up-tape
+ this.tapeInches = 0; // number of inches currently up-tape
this.writeRing = false; // true if write ring is present and tape is writable
this.atBOT = true; // true if tape at BOT
this.atEOT = false; // true if tape at EOT
@@ -173,10 +202,10 @@ B5500MagTapeDrive.prototype.spinReel = function spinReel(inches) {
var circumference = this.reelCircumference*(1 - this.tapeInches/this.maxTapeLength/2);
var angle = inches/circumference*360;
- if (angle >= 33) {
- angle = 33;
- } else if (angle < -33) {
- angle = -33;
+ if (angle >= this.maxSpinAngle) {
+ angle = this.maxSpinAngle;
+ } else if (angle < -this.maxSpinAngle) {
+ angle = -this.maxSpinAngle;
}
this.angle = (this.angle + angle)%360;
@@ -193,7 +222,7 @@ B5500MagTapeDrive.prototype.setAtBOT = function setAtBOT(atBOT) {
this.imgIndex = 0;
this.tapeInches = 0;
this.addClass(this.$$("MTAtBOTLight"), "whiteLit");
- this.progressBar.value = this.imgLength;
+ this.progressBar.value = this.imgMaxInches;
this.reelIcon.style.transform = "rotate(0deg)";
} else {
this.removeClass(this.$$("MTAtBOTLight"), "whiteLit");
@@ -208,7 +237,6 @@ B5500MagTapeDrive.prototype.setAtEOT = function setAtEOT(atEOT) {
if (atEOT ^ this.atEOT) {
this.atEOT = atEOT;
if (atEOT) {
- this.imgIndex = this.imgLength;
this.addClass(this.$$("MTAtEOTLight"), "whiteLit");
this.progressBar.value = 0;
} else {
@@ -234,8 +262,7 @@ B5500MagTapeDrive.prototype.setTapeUnloaded = function setTapeUnloaded() {
this.$$("MTRemoteBtn").disabled = true;
this.$$("MTRewindBtn").disabled = true;
this.$$("MTWriteRingBtn").disabled = true;
- this.$$("MTFileSelector").disabled = false;
- this.$$("MTFileSelector").value = null;
+ this.$$("MTFileName").value = "";
this.removeClass(this.$$("MTRemoteBtn"), "yellowLit");
this.addClass(this.$$("MTLocalBtn"), "yellowLit");
this.removeClass(this.$$("MTWriteRingBtn"), "redLit");
@@ -262,7 +289,6 @@ B5500MagTapeDrive.prototype.setTapeRemote = function setTapeRemote(ready) {
this.$$("MTRemoteBtn").disabled = ready;
this.$$("MTWriteRingBtn").disabled = false;
this.$$("MTRewindBtn").disabled = ready;
- this.$$("MTFileSelector").disabled = true;
this.ready = ready;
if (ready) {
this.tapeState = this.tapeRemote;
@@ -285,13 +311,6 @@ B5500MagTapeDrive.prototype.setWriteRing = function setWriteRing(writeRing) {
switch (this.tapeState) {
case this.tapeLocal:
- this.writeRing = writeRing;
- if (writeRing) {
- this.addClass(this.$$("MTWriteRingBtn"), "redLit");
- } else {
- this.removeClass(this.$$("MTWriteRingBtn"), "redLit");
- }
- break;
case this.tapeRemote:
if (this.writeRing && !writeRing) {
this.writeRing = false;
@@ -301,13 +320,361 @@ B5500MagTapeDrive.prototype.setWriteRing = function setWriteRing(writeRing) {
}
};
+/**************************************/
+B5500MagTapeDrive.prototype.loadTape = function loadTape() {
+ /* Loads a tape into memory based on selections in the MTLoad window */
+ var $$$ = null; // getElementById shortcut for loader window
+ var doc = null; // loader window.document
+ var eotInches = 0; // tape inches until EOT marker
+ var file = null; // FileReader instance
+ var fileSelect = null; // file picker element
+ var formatSelect = null; // tape format list element
+ var maxInches = 0; // maximum tape inches in tape image
+ var mt = this; // this B5500MagTapeDrive instance
+ var tapeFormat = ""; // tape format code (bcd, aod, aev, etc.)
+ var tapeInches = 0; // selected tape length in inches
+ var tapeLengthSelect = null; // tape length list element
+ var win = this.window.open("B5500MagTapeLoadPanel.html", this.mnemonic + "Load",
+ "scrollbars=no,resizable,width=508,height=112");
+ var writeRing = false; // true if write-enabled
+ var writeRingCheck = null; // tape write ring checkbox element
+
+ function fileSelector_onChange(ev) {
+ /* Handle the onchange event when a file is selected */
+ var fileExt;
+ var fileName;
+ var x;
+
+ file = ev.target.files[0];
+ fileName = file.name;
+ x = fileName.lastIndexOf(".");
+ fileExt = (x > 0 ? fileName.substring(x) : "");
+ writeRingCheck.checked = false;
+ tapeLengthSelect.disabled = true;
+
+ switch (fileExt) {
+ case ".bcd":
+ tapeFormat = "bcd";
+ break;
+ case ".tap":
+ tapeFormat = "tap";
+ break;
+ default:
+ tapeFormat = "aod";
+ break;
+ } // switch fileExt
+
+ for (x=formatSelect.length-1; x>=0; x--) {
+ if (formatSelect.options[x].value == tapeFormat) {
+ formatSelect.selectedIndex = x;
+ break;
+ }
+ } // for x
+ }
+
+ function finishLoad() {
+ /* Finishes the tape loading process and closes the loader window */
+
+ mt.imgIndex = 0;
+ mt.imgLength = mt.image.length;
+ mt.tapeInches = 0;
+ mt.imgEOTInches = eotInches;
+ mt.imgMaxInches = tapeInches;
+ mt.progressBar.max = mt.imgMaxInches;
+ mt.progressBar.value = mt.imgMaxInches;
+ mt.removeClass(mt.$$("MTUnloadedLight"), "whiteLit");
+ mt.setAtEOT(false);
+ mt.setAtBOT(true);
+ mt.tapeState = mt.tapeLocal; // setTapeRemote() requires it not be unloaded
+ mt.setTapeRemote(false);
+ mt.reelIcon.style.visibility = "visible";
+
+ mt.imgWritten = false;
+ mt.writeRing = writeRing;
+ if (writeRing) {
+ mt.addClass(mt.$$("MTWriteRingBtn"), "redLit");
+ } else {
+ mt.removeClass(mt.$$("MTWriteRingBtn"), "redLit");
+ }
+
+ win.close();
+ }
+
+ function bcdLoader_onLoad(ev) {
+ /* Loads a ".bcd" tape image into the drive */
+ var blockLength;
+ var image = new Uint8Array(ev.target.result);
+ var imageSize;
+ var x;
+
+ mt.imgTopIndex = image.length;
+ if (writeRing) {
+ eotInches = tapeInches;
+ tapeInches += mt.postEOTLength;
+ imageSize = tapeInches*mt.density;
+ if (image.length > imageSize) {
+ eotInches = image.length/mt.density;
+ imageSize = image.length + mt.postEOTLength*mt.density;
+ tapeInches = imageSize/mt.density;
+ }
+ mt.image = new Uint8Array(new ArrayBuffer(imageSize));
+ for (x=image.length-1; x>=0; x--) {
+ mt.image[x] = image[x];
+ }
+ } else {
+ mt.image = image;
+ imageSize = image.length;
+ tapeInches = 0;
+ x = 0;
+ while (x < imageSize) {
+ x++;
+ blockLength = 1;
+ while (x < imageSize && image[x] < 0x80) {
+ x++;
+ blockLength++;
+ } // while for blockLength
+ tapeInches += blockLength/mt.density + mt.gapLength;
+ } // while for imageSize
+ eotInches = tapeInches + mt.postEOTLength;
+ }
+ finishLoad();
+ }
+
+ function blankLoader() {
+ /* Loads a blank tape image into the drive */
+
+ writeRing = true;
+ eotInches = tapeInches;
+ tapeInches += mt.postEOTLength;
+ mt.image = new Uint8Array(new ArrayBuffer(tapeInches*mt.density));
+ mt.image[0] = 0x81; // put a little noise on the tape to avoid blank-tape timeouts
+ mt.image[1] = 0x03;
+ mt.image[2] = 0x8F;
+ mt.imgTopIndex = 3;
+ finishLoad();
+ }
+
+ function tapLoader_onLoad(ev) {
+ /* Loads a ".tap" tape image into the drive */
+
+ /* To be Provided */
+ }
+
+ function textLoader_onLoad(ev) {
+ /* Loads a text image as either odd or even parity bcd data */
+ var block; // ANSI text of current block
+ var blockLength; // length of current ASCII block
+ var eolRex = /([^\n\r\f]*)((:?\r[\n\f]?)|\n|\f)?/g;
+ var image = ev.target.result; // ANSI tape image
+ var imageLength = image.length; // length of ANSI tape image
+ var imageSize; // size of final tape image [bytes]
+ var inches = 0; // tape inches occupied by image data
+ var index = 0; // image index of next ANSI block
+ var match; // result of eolRex.exec()
+ var offset = 0; // index into mt.image
+ var table = (tapeFormat == "aev" ? mt.bcdXlateOutEven : mt.bcdXlateOutOdd);
+ var x; // for loop index
+
+ if (!writeRing) {
+ imageSize = imageLength;
+ } else {
+ eotInches = tapeInches;
+ tapeInches += mt.postEOTLength;
+ imageSize = tapeInches*mt.density;
+ if (imageLength > imageSize) {
+ eotInches = imageLength/mt.density;
+ imageSize = imageLength + mt.postEOTLength*mt.density;
+ tapeInches = imageSize/mt.density;
+ }
+ }
+
+ mt.image = new Uint8Array(new ArrayBuffer(imageSize));
+ do {
+ eolRex.lastIndex = index;
+ match = eolRex.exec(image);
+ if (!match) {
+ break;
+ } else {
+ index += match[0].length;
+ block = match[1];
+ blockLength = block.length;
+ inches += blockLength/mt.density + mt.gapLength;
+ if (block == "}") {
+ mt.image[offset++] = mt.bcdTapeMark;
+ } else if (blockLength > 0) {
+ mt.image[offset++] = table[block.charCodeAt(0) & 0x7F] | 0x80;
+ for (x=1; x= bufLength) {
+ text.appendChild(doc.createTextNode(String.fromCharCode.apply(null, buf.subarray(0, bufIndex))));
+ bufIndex = 0;
+ }
+ if (c > 0) {
+ buf[bufIndex++] = table[c];
+ }
+ x++;
+ if (x < imgLength) {
+ c = image[x];
+ } else {
+ break;
+ }
+ } while (c < 0x80);
+ buf[bufIndex++] = 0x0A;
+ text.appendChild(doc.createTextNode(String.fromCharCode.apply(null, buf.subarray(0, bufIndex))));
+ } while (x < imgLength);
+
+ mt.setTapeUnloaded();
+ }
+
+ win.moveTo((screen.availWidth-win.outerWidth)/2, (screen.availHeight-win.outerHeight)/2);
+ win.focus();
+ doc = win.document;
+ doc.open();
+ doc.write("Converting... please wait...
");
+ doc.close();
+ doc.title = "B5500 " + this.mnemonic + " Unload Tape";
+ setCallback(unloadDriver, this, 20); // give the message time to display
+};
+
/**************************************/
B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
/* Rewinds the tape. Makes the drive not-ready and delays for an appropriate amount
of time depending on how far up-tape we are. If makeReady is true [valid only when
called from this.rewind()], then readies the unit again when the rewind is complete */
var inches;
- var inchFactor = this.imgIndex/this.tapeInches;
var lastStamp = new Date().getTime();
var updateInterval = 30; // ms
@@ -332,7 +699,7 @@ B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
inches = interval/1000*this.rewindSpeed;
this.tapeInches -= inches;
this.spinReel(-inches);
- this.progressBar.value = this.imgLength - this.tapeInches*inchFactor;
+ this.progressBar.value = this.imgMaxInches - this.tapeInches;
lastStamp = stamp;
this.timer = setCallback(rewindDelay, this, updateInterval);
} else {
@@ -360,16 +727,18 @@ B5500MagTapeDrive.prototype.tapeRewind = function tapeRewind(makeReady) {
B5500MagTapeDrive.prototype.MTUnloadBtn_onclick = function MTUnloadBtn_onclick(ev) {
/* Handle the click event for the UNLOAD button */
- this.setTapeUnloaded();
+ if (this.imgWritten && this.window.confirm("Do you want to save the tape image data?")) {
+ this.unloadTape(); // will do setTapeUnloaded() afterwards
+ } else {
+ this.setTapeUnloaded();
+ }
};
/**************************************/
B5500MagTapeDrive.prototype.MTLoadBtn_onclick = function MTLoadBtn_onclick(ev) {
/* Handle the click event for the LOAD button */
- var ck = new MouseEvent("click", {cancelable:true, bubbles:false, detail:{}});
- this.$$("MTFileSelector").value = null; // reset the control so the same file can be reloaded
- this.$$("MTFileSelector").dispatchEvent(ck);// click the file selector's Browse... button
+ this.loadTape();
};
/**************************************/
@@ -400,35 +769,6 @@ B5500MagTapeDrive.prototype.MTRewindBtn_onclick = function MTRewindBtn(ev) {
this.tapeRewind(false);
};
-/**************************************/
-B5500MagTapeDrive.prototype.fileSelector_onChange = function fileSelector_onChange(ev) {
- /* Handle the onchange event when a file is selected. Loads the
- file and puts the drive in Local state */
- var tape;
- var f = ev.target.files[0];
- var that = this;
-
- function fileLoader_onLoad(ev) {
- /* Handle the onload event for the ArrayBuffer FileReader */
-
- that.image = new Uint8Array(ev.target.result);
- that.imgIndex = 0;
- that.imgLength = that.image.length;
- that.progressBar.max = that.imgLength;
- that.removeClass(that.$$("MTUnloadedLight"), "whiteLit");
- that.tapeState = that.tapeLocal;// setTapeRemote() requires it not to be unloaded
- that.setAtEOT(false);
- that.setAtBOT(true);
- that.setTapeRemote(false);
- that.setWriteRing(false); // read-only for now...
- that.reelIcon.style.visibility = "visible";
- }
-
- tape = new FileReader();
- tape.onload = fileLoader_onLoad;
- tape.readAsArrayBuffer(f);
-};
-
/**************************************/
B5500MagTapeDrive.prototype.buildErrorMask = function buildErrorMask(chars) {
/* Constructs the final error mask from this.errorMask, the number of residual
@@ -439,7 +779,7 @@ B5500MagTapeDrive.prototype.buildErrorMask = function buildErrorMask(chars) {
if (this.atBOT) {
mask |= 0x80000; // tape at BOT
} else if (this.atEOT) {
- mask |= 0x40000; // tape at EOT
+ mask |= 0x40020; // tape at EOT
}
this.errorMask = mask;
return mask;
@@ -489,9 +829,6 @@ B5500MagTapeDrive.prototype.bcdSpaceForward = function bcdSpaceForward(checkEOF)
}
}
this.imgIndex = imgIndex;
- if (imgIndex >= imgLength) {
- this.setAtEOT(true);
- }
};
/**************************************/
@@ -615,9 +952,6 @@ B5500MagTapeDrive.prototype.bcdReadForward = function bcdReadForward(oddParity)
}
this.imgIndex = imgIndex;
this.bufIndex = bufIndex;
- if (imgIndex >= imgLength) {
- this.setAtEOT(true);
- }
return bufIndex;
};
@@ -703,7 +1037,49 @@ B5500MagTapeDrive.prototype.bcdReadBackward = function bcdReadBackward(oddParity
};
/**************************************/
-B5500MagTapeDrive.prototype.beforeUnload = function beforeUnload(ev) {
+B5500MagTapeDrive.prototype.bcdWrite = function bcdWrite(oddParity) {
+ /* Writes the next block to the .bcd tape (this.image) in memory, translating
+ the character frames from ANSI character codes based on the translation
+ table "xlate". The translated data is stored at the current offset in
+ this.buffer. The start of a block is indicated by setting its high-order bit set.
+ oddParity 0=Alpha (even parity), 1=Binary (odd parity) write
+ Exits with the image index pointing beyond the last frame of the block (or beyond
+ the end of the image blob if at the end). Returns the number of characters written
+ to the IOUnit buffer */
+ var buffer = this.buffer; // IOUnit buffer
+ var bufLength = this.bufLength // IOUnit buffer length
+ var bufIndex = 0; // current IOUnit buffer offset
+ var image = this.image; // tape image
+ var imgLength = this.imgLength; // tape image length
+ var imgIndex = this.imgIndex; // current tape image offset
+ var xlate = (oddParity ? this.bcdXlateOutOdd : this.bcdXlateOutEven);
+
+ if (imgIndex >= imgLength) {
+ this.errorMask |= 0x04; // report not ready if beyond end of tape
+ } else {
+ if (this.atBOT) {
+ this.setAtBOT(false);
+ }
+ image[imgIndex++] = xlate[buffer[bufIndex++] & 0x7F] | 0x80;
+ while (bufIndex < bufLength) {
+ if (imgIndex >= imgLength) {
+ this.errorMask |= 0x04; // report not ready beyond end of tape
+ break;
+ } else {
+ image[imgIndex++] = xlate[buffer[bufIndex++] & 0x7F];
+ }
+ } // while
+ }
+ this.imgIndex = imgIndex;
+ this.bufIndex = bufIndex;
+ if (imgIndex > this.imgTopIndex) {
+ this.imgTopIndex = imgIndex;
+ }
+ return bufIndex;
+};
+
+/**************************************/
+B5500MagTapeDrive.prototype.tapeDriveBeforeUnload = function tapeDriveBeforeUnload(ev) {
var msg = "Closing this window will make the device unusable.\n" +
"Suggest you stay on the page and minimize this window instead";
@@ -723,43 +1099,39 @@ B5500MagTapeDrive.prototype.tapeDriveOnLoad = function tapeDriveOnLoad() {
this.progressBar = this.$$("MTProgressBar");
this.reelIcon = this.$$("MTReel");
- this.window.addEventListener("beforeunload", this.beforeUnload, false);
+ this.window.addEventListener("beforeunload", this.tapeDriveBeforeUnload, false);
this.tapeState = this.tapeLocal; // setTapeUnloaded() requires it to be in local
this.atBOT = true; // and also at BOT
this.setTapeUnloaded();
- this.$$("MTUnloadBtn").addEventListener("click", function startClick(ev) {
+ this.$$("MTUnloadBtn").addEventListener("click", function unloadBtn(ev) {
that.MTUnloadBtn_onclick(ev);
}, false);
- this.$$("MTLoadBtn").addEventListener("click", function stopClick(ev) {
+ this.$$("MTLoadBtn").addEventListener("click", function loadBtn(ev) {
that.MTLoadBtn_onclick(ev);
}, false);
- this.$$("MTRemoteBtn").addEventListener("click", function startClick(ev) {
+ this.$$("MTRemoteBtn").addEventListener("click", function remoteBtn(ev) {
that.MTRemoteBtn_onclick(ev);
}, false);
- this.$$("MTLocalBtn").addEventListener("click", function stopClick(ev) {
+ this.$$("MTLocalBtn").addEventListener("click", function localBtn(ev) {
that.MTLocalBtn_onclick(ev);
}, false);
- this.$$("MTWriteRingBtn").addEventListener("click", function eofClick(ev) {
+ this.$$("MTWriteRingBtn").addEventListener("click", function writeRingBtn(ev) {
that.MTWriteRingBtn_onclick(ev);
}, false);
- this.$$("MTRewindBtn").addEventListener("click", function eofClick(ev) {
+ this.$$("MTRewindBtn").addEventListener("click", function rewindBtn(ev) {
that.MTRewindBtn_onclick(ev);
}, false);
this.progressBar.addEventListener("click", function progressClick(ev) {
that.MTProgressBar_onclick(ev);
}, false);
-
- this.$$("MTFileSelector").addEventListener("change", function fileSelectorChange(ev) {
- that.fileSelector_onChange(ev);
- }, false);
};
/**************************************/
@@ -768,7 +1140,7 @@ B5500MagTapeDrive.prototype.read = function read(finish, buffer, length, mode, c
returns those conditions. Otherwise, attempts to read the next block from the tape.
mode 0=Alpha (even parity), 1=Binary (odd parity) read
control 0=forward, 1=backward read
- At present, this supports only .bcd tape images */
+ */
var count; // number of characters read into IOUnit buffer
var imgCount = this.imgIndex; // number of characters passed on tape
var inches = 0; // block length including gap [inches]
@@ -790,14 +1162,21 @@ B5500MagTapeDrive.prototype.read = function read(finish, buffer, length, mode, c
residue = 7 - count % 8;
imgCount -= this.imgIndex;
inches = -imgCount/this.density - this.gapLength;
+ this.tapeInches += inches;
+ if (this.atEOT && this.tapeInches < this.imgEOTInches) {
+ this.setAtEOT(false);
+ }
} else {
count = this.bcdReadForward(mode);
residue = count % 8;
imgCount = this.imgIndex - imgCount;
inches = imgCount/this.density + this.gapLength;
+ this.tapeInches += inches;
+ if (!this.atEOT && this.tapeInches > this.imgEOTInches) {
+ this.setAtEOT(true);
+ }
}
- this.tapeInches += inches;
this.buildErrorMask(residue);
this.timer = setCallback(function readDelay() {
this.busy = false;
@@ -806,8 +1185,8 @@ B5500MagTapeDrive.prototype.read = function read(finish, buffer, length, mode, c
this.initiateStamp - new Date().getTime());
this.spinReel(inches);
- if (this.imgIndex < this.imgLength) {
- this.progressBar.value = this.imgLength-this.imgIndex;
+ if (this.tapeInches < this.imgMaxInches) {
+ this.progressBar.value = this.imgMaxInches - this.tapeInches;
} else {
this.progressBar.value = 0;
}
@@ -825,7 +1204,7 @@ B5500MagTapeDrive.prototype.space = function space(finish, length, control) {
returns those conditions. Otherwise, attempts to space over the next block
from the tape. Parity errors are ignored.
control 0=forward, 1=backward space
- At present, this supports only .bcd tape images */
+ */
var imgCount = this.imgIndex; // number of characters passed on tape
var inches = 0; // block length including gap [inches]
@@ -841,13 +1220,20 @@ B5500MagTapeDrive.prototype.space = function space(finish, length, control) {
this.bcdSpaceBackward(true);
imgCount -= this.imgIndex;
inches = -imgCount/this.density - this.gapLength;
+ this.tapeInches += inches;
+ if (this.atEOT && this.tapeInches < this.imgEOTInches) {
+ this.setAtEOT(false);
+ }
} else {
this.bcdSpaceForward(true);
imgCount = this.imgIndex - imgCount;
inches = imgCount/this.density + this.gapLength;
+ this.tapeInches += inches;
+ if (!this.atEOT && this.tapeInches > this.imgEOTInches) {
+ this.setAtEOT(true);
+ }
}
- this.tapeInches += inches;
this.buildErrorMask(0);
this.timer = setCallback(function readDelay() {
this.busy = false;
@@ -856,8 +1242,8 @@ B5500MagTapeDrive.prototype.space = function space(finish, length, control) {
this.initiateStamp - new Date().getTime());
this.spinReel(inches);
- if (this.imgIndex < this.imgLength) {
- this.progressBar.value = this.imgLength-this.imgIndex;
+ if (this.tapeInches < this.imgMaxInches) {
+ this.progressBar.value = this.imgMaxInches - this.tapeInches;
} else {
this.progressBar.value = 0;
}
@@ -869,16 +1255,101 @@ B5500MagTapeDrive.prototype.space = function space(finish, length, control) {
/**************************************/
B5500MagTapeDrive.prototype.write = function write(finish, buffer, length, mode, control) {
- /* Initiates a write operation on the unit */
+ /* Initiates a write operation on the unit. If the drive is busy, not ready or has
+ no write ring, returns those conditions. Otherwise, attempts to write the next block
+ to the tape. mode 0=Alpha (even parity), 1=Binary (odd parity) write */
+ var count; // number of characters read into IOUnit buffer
+ var imgCount = this.imgIndex; // number of characters passed on tape
+ var inches = 0; // block length including gap [inches]
+ var residue; // residual characters in last word read
- finish(0x04, 0); // report unit not ready
+ this.errorMask = 0;
+ if (this.busy) {
+ finish(0x01, 0); // report unit busy
+ } else if (!this.ready) {
+ finish(0x04, 0); // report unit not ready
+ } else if (!this.writeRing) {
+ finish(0x50, 0); // RD bits 26 & 28 => no write ring, don't return Mod III bits
+ } else {
+ this.busy = true;
+ this.buffer = buffer;
+ this.bufLength = length;
+ this.bufIndex = 0;
+
+ count = this.bcdWrite(mode);
+ residue = count % 8;
+ imgCount = this.imgIndex - imgCount;
+ inches = imgCount/this.density + this.gapLength;
+ this.tapeInches += inches;
+ if (!this.atEOT && this.tapeInches > this.imgEOTInches) {
+ this.setAtEOT(true);
+ }
+
+ this.imgWritten = true;
+ this.buildErrorMask(residue);
+ this.timer = setCallback(function writeDelay() {
+ this.busy = false;
+ finish(this.errorMask, count);
+ }, this, (imgCount/this.charsPerSec + this.startStopTime)*1000 +
+ this.initiateStamp - new Date().getTime());
+
+ this.spinReel(inches);
+ if (this.tapeInches < this.imgMaxInches) {
+ this.progressBar.value = this.imgMaxInches - this.tapeInches;
+ } else {
+ this.progressBar.value = 0;
+ }
+ this.buffer = null;
+ }
+ //console.log(this.mnemonic + " write: c=" + control + ", length=" + length + ", mode=" + mode +
+ // ", count=" + count + ", inches=" + this.tapeInches +
+ // ", index=" + this.imgIndex + ", mask=" + this.errorMask.toString(8));
+ //console.log(String.fromCharCode.apply(null, buffer.subarray(0, 80)).substring(0, count));
};
/**************************************/
B5500MagTapeDrive.prototype.erase = function erase(finish, length) {
- /* Initiates an erase operation on the unit */
+ /* Initiates an erase operation on the unit. If the drive is busy, not ready,
+ or has no write ring, then returns those conditions. Otherwise, does nothing
+ to the tape image, as lengths of blank tape less than 9 feet in length are
+ not "seen" by the I/O Unit. Delays an appropriate amount of time for the
+ length of the erasure */
+ var inches; // erase length [inches]
- finish(0x04, 0); // report unit not ready
+ this.errorMask = 0;
+ if (this.busy) {
+ finish(0x01, 0); // report unit busy
+ } else if (!this.ready) {
+ finish(0x04, 0); // report unit not ready
+ } else if (!this.writeRing) {
+ finish(0x50, 0); // RD bits 26 & 28 => no write ring, don't return Mod III bits
+ } else {
+ this.busy = true;
+
+ inches = length/this.density;
+ this.tapeInches += inches;
+ if (!this.atEOT && this.tapeInches > this.imgEOTInches) {
+ this.setAtEOT(true);
+ }
+
+ this.imgWritten = true;
+ this.buildErrorMask(0);
+ this.timer = setCallback(function eraseDelay() {
+ this.busy = false;
+ finish(this.errorMask, 0);
+ }, this, (length/this.charsPerSec + this.startStopTime)*1000 +
+ this.initiateStamp - new Date().getTime());
+
+ this.spinReel(inches);
+ if (this.tapeInches < this.imgMaxInches) {
+ this.progressBar.value = this.imgMaxInches - this.tapeInches;
+ } else {
+ this.progressBar.value = 0;
+ }
+ }
+ //console.log(this.mnemonic + " erase: c=" + control + ", length=" + length +
+ // ", inches=" + this.tapeInches +
+ // ", index=" + this.imgIndex + ", mask=" + this.errorMask.toString(8));
};
/**************************************/
@@ -927,7 +1398,7 @@ B5500MagTapeDrive.prototype.writeInterrogate = function writeInterrogate(finish,
finish(0x04, 0); // report unit not ready
} else {
if (this.writeRing) {
- this.buildErrorMask(0, true);
+ this.buildErrorMask(0);
} else {
this.errorMask |= 0x50; // RD bits 26 & 28 => no write ring, don't return Mod III bits
}
@@ -943,6 +1414,6 @@ B5500MagTapeDrive.prototype.shutDown = function shutDown() {
if (this.timer) {
clearCallback(this.timer);
}
- this.window.removeEventListener("beforeunload", this.beforeUnload, false);
+ this.window.removeEventListener("beforeunload", this.tapeDriveBeforeUnload, false);
this.window.close();
};
diff --git a/webUI/B5500MagTapeLoadPanel.html b/webUI/B5500MagTapeLoadPanel.html
new file mode 100644
index 0000000..d68a89c
--- /dev/null
+++ b/webUI/B5500MagTapeLoadPanel.html
@@ -0,0 +1,48 @@
+
+
+B5500 Emulator Magnetic Tape Loader
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Format
+
+
+
+
+
+
+
+ Tape Length
+
+
+
+
+
+
+
+
+