mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-04-14 08:59:13 +00:00
Release emulator version 0.19:
1. Implement write to tape and persistence of tape image data.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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("<h3>Build .bdc Tape Image Translate Tables</h3>");
|
||||
|
||||
// Initialize the tables with invalid parity codes
|
||||
for (x=0; x<128; x++) {
|
||||
bcdEven[x] = bcdOdd[x] = 0xFF;
|
||||
bcdEven[x] = ansiEven[x] = bcdOdd[x] = ansiOdd[x] = 0xFF;
|
||||
}
|
||||
|
||||
// Build the even/odd parity codes
|
||||
@@ -94,11 +99,13 @@ for (c=0; c<64; c++) {
|
||||
odd = 1-parity(c);
|
||||
x = c | (odd << 6);
|
||||
bcdOdd[x] = BICtoANSI[c].charCodeAt(0);
|
||||
ansiOdd[bcdOdd[x]] = x;
|
||||
|
||||
// Even parity translates
|
||||
even = parity(c);
|
||||
y = c | (even << 6);
|
||||
bcdEven[y] = BICtoANSI[c].charCodeAt(0);
|
||||
bcdEven[y] = BCLtoANSI[c].charCodeAt(0);
|
||||
ansiEven[bcdEven[y]] = y;
|
||||
|
||||
document.write("<tr><td>");
|
||||
document.write(pic9n(c.toString(8), 2));
|
||||
@@ -115,20 +122,40 @@ for (c=0; c<64; c++) {
|
||||
}
|
||||
document.write("</table>");
|
||||
|
||||
document.write("<h3>Odd Parity Table</h3><code>");
|
||||
document.write("<h3>BCD Odd Parity BIC to ANSI Table</h3><code>");
|
||||
|
||||
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("<br>");
|
||||
}
|
||||
}
|
||||
document.write("</code>");
|
||||
|
||||
document.write("<h3>Even Parity Table</h3><code>");
|
||||
document.write("<h3>BCD Odd Parity ANSI to BIC Table</h3><code>");
|
||||
|
||||
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("<br>");
|
||||
}
|
||||
}
|
||||
document.write("</code>");
|
||||
|
||||
document.write("<h3>BCD Even Parity BCL to ANSI Table</h3><code>");
|
||||
|
||||
for (x=0; x<128; x++) {
|
||||
document.write("0x" + pic9n(bcdEven[x].toString(16).toUpperCase(), 2) + ", ");
|
||||
if (x % 16 == 15) {
|
||||
document.write("<br>");
|
||||
}
|
||||
}
|
||||
document.write("</code>");
|
||||
|
||||
document.write("<h3>BCD Even Parity ANSI to BCL Table</h3><code>");
|
||||
|
||||
for (x=0; x<128; x++) {
|
||||
document.write("0x" + pic9n(ansiEven[x].toString(16).toUpperCase(), 2) + ", ");
|
||||
if (x % 16 == 15) {
|
||||
document.write("<br>");
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<span id=MTAtEOTLight class=annunciator>AT EOT</span>
|
||||
<span id=MTRewindingLight class=annunciator>REWINDING</span>
|
||||
|
||||
<input id=MTFileSelector type=file size=60>
|
||||
<input id=MTFileName type=text size=60 READONLY>
|
||||
|
||||
<progress id=MTProgressBar min=0 max=100 value=0 title="Click to clear input hopper"></progress>
|
||||
</div>
|
||||
|
||||
@@ -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 <input type=file> 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<blockLength; x++) {
|
||||
mt.image[offset++] = table[block.charCodeAt(x) & 0x7F];
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (index < imageLength);
|
||||
|
||||
mt.imgTopIndex = offset;
|
||||
if (!writeRing) {
|
||||
tapeInches = inches;
|
||||
eotInches = tapeInches + mt.postEOTLength;
|
||||
}
|
||||
finishLoad();
|
||||
}
|
||||
|
||||
function tapeLoadOK(ev) {
|
||||
/* Handler for the OK button. Does the actual tape load */
|
||||
var tape;
|
||||
|
||||
tapeFormat = formatSelect.value;
|
||||
if (!(file || tapeFormat == "blank")) {
|
||||
win.alert("File must be selected unless loading a blank tape");
|
||||
} else {
|
||||
tapeInches = (parseInt(tapeLengthSelect.value) || 2400)*12;
|
||||
writeRing = writeRingCheck.checked;
|
||||
mt.$$("MTFileName").value = (file ? file.name : "");
|
||||
|
||||
switch (tapeFormat) {
|
||||
case "aod":
|
||||
case "aev":
|
||||
tape = new FileReader();
|
||||
tape.onload = textLoader_onLoad;
|
||||
tape.readAsText(file);
|
||||
break;
|
||||
case "bcd":
|
||||
tape = new FileReader();
|
||||
tape.onload = bcdLoader_onLoad;
|
||||
tape.readAsArrayBuffer(file);
|
||||
break;
|
||||
case "tap":
|
||||
tape = new FileReader();
|
||||
tape.onload = tapLoader_onLoad;
|
||||
tape.readAsArrayBuffer(file);
|
||||
break;
|
||||
default:
|
||||
mt.$$("MTFileName").value = (file ? file.name : "(blank tape)");
|
||||
blankLoader();
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
}
|
||||
|
||||
function tapeLoadOnLoad (ev) {
|
||||
/* Driver for the tape loader window */
|
||||
|
||||
doc = win.document;
|
||||
$$$ = function $$$(id) {
|
||||
return doc.getElementById(id);
|
||||
};
|
||||
|
||||
fileSelect = $$$("MTLoadFileSelector");
|
||||
formatSelect = $$$("MTLoadFormatSelect");
|
||||
writeRingCheck = $$$("MTLoadWriteRingCheck");
|
||||
tapeLengthSelect = $$$("MTLoadTapeLengthSelect")
|
||||
|
||||
doc.title = "B5500 " + mt.mnemonic + " Tape Loader";
|
||||
fileSelect.addEventListener("change", fileSelector_onChange, false);
|
||||
|
||||
formatSelect.addEventListener("change", function loadFormatSelect(ev) {
|
||||
tapeFormat = ev.target.value;
|
||||
if (tapeFormat == "blank") {
|
||||
file = null;
|
||||
fileSelect.value = null;
|
||||
writeRingCheck.checked = true;
|
||||
tapeLengthSelect.disabled = false;
|
||||
tapeLengthSelect.selectedIndex = tapeLengthSelect.length-1;
|
||||
}
|
||||
}, false);
|
||||
|
||||
writeRingCheck.addEventListener("click", function loadWriteRingCheck(ev) {
|
||||
tapeLengthSelect.disabled = !ev.target.checked;
|
||||
}, false);
|
||||
|
||||
$$$("MTLoadOKBtn").addEventListener("click", tapeLoadOK, false);
|
||||
|
||||
$$$("MTLoadCancelBtn").addEventListener("click", function loadCancelBtn(ev) {
|
||||
file = null;
|
||||
mt.$$("MTFileName").value = "";
|
||||
win.close();
|
||||
}, false);
|
||||
}
|
||||
|
||||
mt.$$("MTLoadBtn").disabled = true;
|
||||
win.moveTo((screen.availWidth-win.outerWidth)/2, (screen.availHeight-win.outerHeight)/2);
|
||||
win.focus();
|
||||
win.addEventListener("load", tapeLoadOnLoad, false);
|
||||
win.addEventListener("unload", function tapeLoadUnload(ev) {
|
||||
if (win.closed) {
|
||||
mt.$$("MTLoadBtn").disabled = (mt.tapeState != mt.tapeUnloaded);
|
||||
}
|
||||
}, false);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500MagTapeDrive.prototype.unloadTape = function unloadTape() {
|
||||
/* Reformats the tape image data as ASCII text and displays it in a new
|
||||
window so the user can save or copy/paste it elsewhere */
|
||||
var doc = null; // loader window.document
|
||||
var mt = this; // tape drive object
|
||||
var win = this.window.open("", this.mnemonic + "Unload",
|
||||
"scrollbars=no,resizable,width=800,height=600");
|
||||
|
||||
function unloadDriver() {
|
||||
/* Converts the tape image to ASCII once the window has displayed the
|
||||
waiting message */
|
||||
var buf = new Uint8Array(new ArrayBuffer(8192));
|
||||
var bufIndex; // offset into ASCII block data
|
||||
var bufLength = buf.length-2; // max usable block size
|
||||
var c; // current image byte;
|
||||
var image = mt.image; // tape image data
|
||||
var imgLength = mt.imgTopIndex; // tape image active length
|
||||
var table; // even/odd parity translate table
|
||||
var text = doc.getElementById("TapeText");
|
||||
var x = 0; // image data index
|
||||
|
||||
while (text.firstChild) {
|
||||
text.removeChild(text.firstChild);
|
||||
}
|
||||
|
||||
c = image[x];
|
||||
do {
|
||||
c &= 0x7F;
|
||||
table = (mt.bcdXlateInEven[c] < 0xFF ? mt.bcdXlateInEven : mt.bcdXlateInOdd);
|
||||
bufIndex = 0;
|
||||
do {
|
||||
if (bufIndex >= 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("<html><head></head><body><pre id=TapeText>Converting... please wait...</pre></body></html>");
|
||||
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 <input type=file> 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();
|
||||
};
|
||||
|
||||
48
webUI/B5500MagTapeLoadPanel.html
Normal file
48
webUI/B5500MagTapeLoadPanel.html
Normal file
@@ -0,0 +1,48 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>B5500 Emulator Magnetic Tape Loader</title>
|
||||
<meta name="Author" content="Nigel Williams & Paul Kimpel">
|
||||
<!-- 2014-01-01 Original version, cloned from B5500MagTapeDrive.html -->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<meta http-equiv="Content-Script-Type" content="text/javascript">
|
||||
<meta http-equiv="Content-Style-Type" content="text/css">
|
||||
<link id=defaultStyleSheet rel=stylesheet type="text/css" href="B5500MagTapeDrive.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id=MTLoaderDiv>
|
||||
<div id=MTLoaderInnerDiv>
|
||||
<input id=MTLoadFileSelector type=file size=60>
|
||||
|
||||
<div id=MTLoadFormatGroup>
|
||||
Format
|
||||
<select id=MTLoadFormatSelect>
|
||||
<option value="blank" selected>(blank tape)
|
||||
<option value="bcd">.bcd Image
|
||||
<!--
|
||||
<option value="tap">.tap Image
|
||||
-->
|
||||
<option value="aev">ASCII Even Parity
|
||||
<option value="aod">ASCII Odd Parity
|
||||
</select>
|
||||
|
||||
<input id=MTLoadWriteRingCheck type=checkbox checked value="1">
|
||||
<label for=MTLoadWriteRingCheck>Write Ring</label>
|
||||
</div>
|
||||
|
||||
<div id=MTLoadTapeLengthGroup>
|
||||
Tape Length
|
||||
<select id=MTLoadTapeLengthSelect>
|
||||
<option value="600">600 feet
|
||||
<option value="1200">1200 feet
|
||||
<option value="2400" selected>2400 feet
|
||||
</select>
|
||||
</div>
|
||||
<button id=MTLoadCancelBtn>Cancel</button>
|
||||
<button id=MTLoadOKBtn>OK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user