mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-02-11 19:05:01 +00:00
373 lines
13 KiB
HTML
373 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<head>
|
|
<title>B5500 LibMaint Extract</title>
|
|
<meta name="Author" content="Paul Kimpel">
|
|
<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">
|
|
|
|
<script>
|
|
"use strict";
|
|
|
|
window.onload = function() {
|
|
var panel = document.getElementById("TextPanel");
|
|
var tapeMark = 0x8F;
|
|
var tapeDir = [];
|
|
|
|
var BICtoANSI = [
|
|
"0", "1", "2", "3", "4", "5", "6", "7",
|
|
"8", "9", "#", "@", "?", ":", ">", "}",
|
|
"+", "A", "B", "C", "D", "E", "F", "G",
|
|
"H", "I", ".", "[", "&", "(", "<", "~",
|
|
"|", "J", "K", "L", "M", "N", "O", "P",
|
|
"Q", "R", "$", "*", "-", ")", ";", "{",
|
|
" ", "/", "S", "T", "U", "V", "W", "X",
|
|
"Y", "Z", ",", "%", "!", "=", "]", "\""];
|
|
|
|
var tapeCtl = {
|
|
data: null,
|
|
offset: 0,
|
|
dataLength: -1,
|
|
eof: false,
|
|
eot: false,
|
|
blockCount: 0,
|
|
blockLength: 0
|
|
};
|
|
|
|
function appendLine(text) {
|
|
/* Appends "text"+NL as a new text node to the panel DOM element */
|
|
var e = document.createTextNode(text + "\n");
|
|
|
|
panel.appendChild(e);
|
|
}
|
|
|
|
function clearPanel() {
|
|
/* Clears the text panel */
|
|
var kid;
|
|
|
|
while (kid = panel.firstChild) {
|
|
panel.removeChild(kid);
|
|
}
|
|
}
|
|
|
|
function parseNumber(s) {
|
|
/* Parses the string "s" as a base-10 number. Returns 0 if it is not a number */
|
|
var n = parseInt(s, 10);
|
|
|
|
return (isNaN(n) ? 0 : n);
|
|
}
|
|
|
|
function rtrim(s) {
|
|
/* Trims trailing spaces from "s" and returns the resulting string */
|
|
var m = s.match(/^(.*?) *$/);
|
|
|
|
return m[1];
|
|
}
|
|
|
|
function readTextBlock(ctl) {
|
|
/* Reads the next block from the tape, translating the character frames to ANSI
|
|
character codes and returning the data as a string. A block is terminated when
|
|
the next frame has its high-order bit set, or the end of the data is reached.
|
|
The string returned is always at least one character in length, unless the block
|
|
is a tapeMark (in which case the "eof" property is set) or the end of the data
|
|
has been reached (in which case the "eof" and "eot" properties are set) */
|
|
var c;
|
|
var data = ctl.data;
|
|
var limit = ctl.dataLength;
|
|
var text = "";
|
|
var x = ctl.offset;
|
|
|
|
if (x >= limit) {
|
|
ctl.eof = true;
|
|
ctl.eot = true;
|
|
ctl.blockLength = 0;
|
|
} else {
|
|
c = data.getUint8(x);
|
|
if (c == tapeMark) {
|
|
ctl.eof = true;
|
|
ctl.offset = x+1;
|
|
ctl.blockLength = 0;
|
|
} else {
|
|
do {
|
|
text += BICtoANSI[c & 0x3F];
|
|
if (++x < limit) {
|
|
c = data.getUint8(x);
|
|
} else {
|
|
c = tapeMark; // to kill the loop
|
|
}
|
|
} while (c < 128);
|
|
ctl.eof = false;
|
|
ctl.blockLength = x - ctl.offset;
|
|
ctl.offset = x;
|
|
ctl.blockCount++;
|
|
}
|
|
}
|
|
return text;
|
|
}
|
|
|
|
function readWordBlock(ctl) {
|
|
/* Reads the next block from the tape, translating the character frames to an array
|
|
of B5500 binary words and returning the array. A block is terminated when
|
|
the next frame has its high-order bit set, or the end of the data is reached.
|
|
The array returned is always at least one element in length, unless the block
|
|
is a tapeMark (in which case the "eof" property is set) or the end of the data
|
|
has been reached (in which case the "eof" and "eot" properties are set) */
|
|
var c;
|
|
var data = ctl.data;
|
|
var limit = ctl.dataLength;
|
|
var w = 0;
|
|
var words = [];
|
|
var wx = 0;
|
|
var x = ctl.offset;
|
|
|
|
if (x >= limit) {
|
|
ctl.eof = true;
|
|
ctl.eot = true;
|
|
ctl.blockLength = 0;
|
|
} else {
|
|
c = data.getUint8(x);
|
|
if (c == tapeMark) {
|
|
ctl.eof = true;
|
|
ctl.offset = x+1;
|
|
ctl.blockLength = 0;
|
|
} else {
|
|
do {
|
|
if (wx < 8) {
|
|
w = w*64 + (c & 0x3F);
|
|
wx++;
|
|
} else {
|
|
words.push(w);
|
|
w = c & 0x3F;
|
|
wx = 1;
|
|
}
|
|
if (++x < limit) {
|
|
c = data.getUint8(x);
|
|
} else {
|
|
c = tapeMark; // to kill the loop
|
|
}
|
|
} while (c < 128);
|
|
|
|
// Right-justify the last word as necessary
|
|
while (wx++ < 8) {
|
|
w *= 64;
|
|
}
|
|
words.push(w);
|
|
ctl.eof = false;
|
|
ctl.blockLength = x - ctl.offset;
|
|
ctl.offset = x;
|
|
ctl.blockCount++;
|
|
}
|
|
}
|
|
return words;
|
|
}
|
|
|
|
function readTapeLabel(ctl) {
|
|
/* Reads the next block from the tape and determines if it is a B5500 tape label.
|
|
If so, decodes the label into a label object and returns the object */
|
|
var rec;
|
|
var s;
|
|
|
|
var lab = {
|
|
isLabel: false,
|
|
text: "",
|
|
heading: "",
|
|
mfid: "",
|
|
fid: "",
|
|
reel: 0,
|
|
dateWritten:0,
|
|
cycle: 0,
|
|
datePurge: 0,
|
|
sentinel: 0,
|
|
blockCount: 0,
|
|
recordCount:0,
|
|
memdumpKey: 0,
|
|
tapeNumber: ""};
|
|
|
|
rec = readTextBlock(ctl);
|
|
if (!ctl.eof) {
|
|
lab.text = rec;
|
|
if (ctl.blockLength == 80 && (s = rec.substring(0, 8)) == " LABEL ") {
|
|
lab.isLabel = true;
|
|
lab.heading = s;
|
|
lab.mfid = rec.substring(9, 16);
|
|
lab.fid = rec.substring(17, 24);
|
|
lab.reel = parseNumber(rec.substring(24, 27));
|
|
lab.dateWritten = parseNumber(rec.substring(27, 32));
|
|
lab.cycle = parseNumber(rec.substring(32, 34));
|
|
lab.datePurge = parseNumber(rec.substring(34, 39));
|
|
lab.sentinel = parseNumber(rec.substring(39, 40));
|
|
lab.blockCount = parseNumber(rec.substring(40, 45));
|
|
lab.recordCount = parseNumber(rec.substring(45, 52));
|
|
lab.memdumpKey = parseNumber(rec.substring(52, 53));
|
|
lab.tapeNumber = rec.substring(53, 58);
|
|
}
|
|
}
|
|
return lab;
|
|
}
|
|
|
|
function readTapeDirectory(ctl) {
|
|
/* Reads the Lib/Maint tape directory and returns and array of file names, indexed
|
|
starting at 1. If the directory is invalid, returns an empty array */
|
|
var dir = [];
|
|
var done;
|
|
var fid;
|
|
var lab;
|
|
var mfid;
|
|
var rec;
|
|
var w;
|
|
var x;
|
|
|
|
lab = readTapeLabel(ctl);
|
|
if (ctl.eof) {
|
|
appendLine("TapeDir: EOF encountered when tape label expected, block=" + ctl.blockCount);
|
|
} else if (!lab.isLabel) {
|
|
appendLine(lab.text);
|
|
appendLine("TapeDir: Above block encountered when a tape label was expected, block=" + ctl.blockCount);
|
|
} else {
|
|
dir.push(rtrim(lab.mfid) + "/" + rtrim(lab.fid)); // store the tape name in dir[0]
|
|
rec = readTextBlock(ctl);
|
|
if (!ctl.eof) {
|
|
appendLine("TapeDir: EOF expected after starting label, block=" + ctl.blockCount);
|
|
}
|
|
do {
|
|
rec = readTextBlock(ctl);
|
|
if (!ctl.eof) {
|
|
x = 0;
|
|
done = false;
|
|
do {
|
|
if (x+8 > rec.length) {
|
|
appendLine("TapeDir: No terminating entry, block=" + ctl.blockCount + ", x=" + x);
|
|
done = true;
|
|
} else if (rec.substring(x, x+8) == "0000000?") {
|
|
done = true;
|
|
} else if (x+16 > rec.length) {
|
|
appendLine("TapeDir: Truncated directory entry, block=" + ctl.blockCount + ", x=" + x);
|
|
done = true;
|
|
} else {
|
|
mfid = rec.substring(x+1, x+8);
|
|
fid = rec.substring(x+9, x+16);
|
|
dir.push(rtrim(mfid) + "/" + rtrim(fid));
|
|
x += 16;
|
|
}
|
|
} while (!done);
|
|
}
|
|
} while (!ctl.eof);
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
function fileLoader_onLoad(ev) {
|
|
/* Handle the onload event for an ArrayBuffer FileReader */
|
|
var buf = ev.target.result;
|
|
var bytes = buf.byteLength;
|
|
var data = new DataView(buf); // use DataView() to avoid problems with littleendians.
|
|
var text = "";
|
|
var v;
|
|
var x = 0;
|
|
|
|
clearPanel();
|
|
tapeCtl.data = data;
|
|
tapeCtl.offset = 0;
|
|
tapeCtl.dataLength = buf.byteLength;
|
|
tapeCtl.eof = false;
|
|
tapeCtl.eot = false;
|
|
tapeCtl.blockCount = 0;
|
|
|
|
v = readTapeDirectory(tapeCtl);
|
|
for (x=0; x<v.length; x++) {
|
|
appendLine(v[x]);
|
|
}
|
|
|
|
/***************************************************************
|
|
do {
|
|
v = data.getUint8(x);
|
|
if (v & 0x80) {
|
|
appendLine(text);
|
|
panel.appendChild(document.createElement("hr"));
|
|
if (v == tapeMark) {
|
|
text = "\\\\\\\\\\ [EOF] /////";
|
|
} else {
|
|
text = BICtoANSI[v & 0x3F];
|
|
}
|
|
} else {
|
|
if (text.length >= 80) {
|
|
appendLine(text);
|
|
text = "";
|
|
}
|
|
text += BICtoANSI[v & 0x3F];
|
|
}
|
|
} while (++x < bytes);
|
|
appendLine(text);
|
|
***************************************************************/
|
|
|
|
//document.getElementById("RunBtn").disabled = false;
|
|
}
|
|
|
|
function fileSelector_onChange(ev) {
|
|
/* Handle the <input type=file> onchange event when a file is selected */
|
|
var f = ev.target.files[0];
|
|
var reader = new FileReader();
|
|
|
|
document.getElementById("GoBtn").disabled = true;
|
|
|
|
alert("File selected: " + f.name +
|
|
"\nModified " + f.lastModifiedDate +
|
|
"\nType=" + f.type + ", Size=" + f.size + " octets");
|
|
|
|
reader.onload = fileLoader_onLoad;
|
|
reader.readAsArrayBuffer(f);
|
|
}
|
|
|
|
function goBtn_onClick(ev) {
|
|
/* Driver to process the data file */
|
|
|
|
}
|
|
|
|
function checkBrowser() {
|
|
/* Checks whether this browser can support the necessary stuff */
|
|
var missing = "";
|
|
|
|
if (!window.File) {missing += ", File"}
|
|
if (!window.FileReader) {missing += ", FileReader"}
|
|
if (!window.FileList) {missing += ", FileList"}
|
|
if (!window.Blob) {missing += ", Blob"}
|
|
if (!window.ArrayBuffer) {missing += ", ArrayBuffer"}
|
|
if (!window.DataView) {missing += ", DataView"}
|
|
|
|
if (missing.length == 0) {
|
|
return false;
|
|
} else {
|
|
alert("No can do... your browser does not support the following features:\n" + missing.substring(2));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/* Start of window.onload() */
|
|
if (checkBrowser()) {
|
|
return;
|
|
}
|
|
document.getElementById("FileSelector").addEventListener("change", fileSelector_onChange, false);
|
|
document.getElementById("GoBtn").addEventListener("click", goBtn_onClick, false);
|
|
}
|
|
</script>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div style="position:relative; width:100%; height:3em">
|
|
<div style="position:absolute; left:0; top:0; width:auto">
|
|
retro-B5500 LibMaint Tape Extract Utility
|
|
</div>
|
|
<div style="position:absolute; top:0; right:0; width:auto">
|
|
<input id=FileSelector type=file size=60>
|
|
|
|
<input id=GoBtn type=button value=Go disabled>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<pre id=TextPanel>
|
|
</pre>
|
|
|
|
</body>
|
|
</html> |