1
0
mirror of https://github.com/pkimpel/retro-b5500.git synced 2026-02-12 11:17:29 +00:00
Files
pkimpel.retro-b5500/tools/B5500ColdLoader.html

999 lines
41 KiB
HTML

<!DOCTYPE html>
<head>
<title>B5500 Coldstart Disk Subsystem Loader</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>
/***********************************************************************
* retro-b5500/tools B5500ColdLoader.html
************************************************************************
* Copyright (c) 2012, Paul Kimpel.
* Licensed under the MIT License,
* see http://www.opensource.org/licenses/mit-license.php
************************************************************************
* B5500 Coldstart Disk Subsystem Loader.
*
* This script opens an IndexedDB database in the browser (creating it first, if
* necessary) and initializes it as a B5500 Head-per-Track disk file subsystem.
* It creates, as necessary, a separate IDB object store for for the number of
* Electronics Units specified by the "euSet" constant, each consisting of the number
* of 30-word segments (sectors) specified by the "EUn" properties of "euSet".
*
* If the ColdStart box is checked on the page, the disk directory structure on
* EU0 is overwritten with a new, empty directory structure, and a default set of
* the system parameters are created.
*
*
*=======================================================================
* The script then reads a Burroughs B5500 Library/Maintenance tape as one
* large blob and extracts all files, converting the 6-bit B5500 Internal Code
* (BIC) characters to 8-bit ASCII. All files are extracted.
*
* The blob is assumed to be in the so-called ".bcd" format. Each 7-bit frame
* from the tape is represented as one 8-bit unsigned byte. The low-order six
* bits (mask 0x3F) contain the character value. The next bit (mask 0x40) is
* the parity bit, and the high-order bit (mask 0x80) indicates the byte is
* at the start of a physical tape block. Tape marks (EOF) are indicated by a
* block containing a single 0x8F byte.
*
* The extraction process is driven by the tape directory at the beginning of
* the tape volume. Continuation "reels" are not currently supported.
*
* To use, select the .bcd file using the file selection control on the page.
* The script writes a log of activity to the web page.
*
* This version outputs the converted data by opening a browser window for
* each file and inserting the converted text into a <textarea> element in
* that window. From there you can copy the text and paste into another
* program that can save the data to a local filesystem. This approach is
* being used until we can figure out a better way to get data out of a
* browser environment and into a local filesystem. Ugh.
************************************************************************
* 2012-12-29 P.Kimpel
* Original version, from B5500LibMaintExtract.html.
***********************************************************************/
"use strict";
window.onload = function() {
const configName = "CONFIG"; // database configuration store name
const dbName = "B5500DiskUnit"; // IDB database name
const dbVersion = 1; // current IDB database version
const directoryTop = 2000; // start of directory area
const directoryEnd = 3008; // end of directory area
const euSize = 200000; // model I size (5 Storage Units: 6MW or 48MC)
const euPrefix = "EU"; // prefix for EU object store names
const tapeMark = 0x8F; // .bcd file tapemark (EOF) octet code
var config = null; // copy of CONFIG store contents
var disk = null; // the IDB database object
var panel = document.getElementById("TextPanel");
var tapeDir = [];
var euSet = {EU0: euSize, EU1: euSize};
var tapeCtl = {
data: null,
offset: 0,
dataLength: -1,
eof: false,
eot: false,
blockCount: 0,
blockLength: 0};
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 pow2 = [ // powers of 2 from 0 to 52
0x1, 0x2, 0x4, 0x8,
0x10, 0x20, 0x40, 0x80,
0x100, 0x200, 0x400, 0x800,
0x1000, 0x2000, 0x4000, 0x8000,
0x10000, 0x20000, 0x40000, 0x80000,
0x100000, 0x200000, 0x400000, 0x800000,
0x1000000, 0x2000000, 0x4000000, 0x8000000,
0x10000000, 0x20000000, 0x40000000, 0x80000000,
0x100000000, 0x200000000, 0x400000000, 0x800000000,
0x1000000000, 0x2000000000, 0x4000000000, 0x8000000000,
0x10000000000, 0x20000000000, 0x40000000000, 0x80000000000,
0x100000000000, 0x200000000000, 0x400000000000, 0x800000000000,
0x1000000000000, 0x2000000000000, 0x4000000000000, 0x8000000000000,
0x10000000000000];
function $$(id) {
return document.getElementById(id);
}
function bit(word, bit) {
/* Extracts and returns the specified bit from the word */
var e = 47-bit; // word lower power exponent
var p; // bottom portion of word power of 2
if (e > 0) {
return ((word - word % (p = pow2[e]))/p) % 2;
} else {
return word % 2;
}
};
function fieldIsolate(word, start, width) {
/* Extracts a bit field [start:width] from word and returns the field */
var le = 48-start-width; // lower power exponent
var p; // bottom portion of word power of 2
return (le == 0 ? word : (word - word % (p = pow2[le]))/p) % pow2[width];
};
function spout(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 padToLength(text, len) {
/* Converts the input string "text" to exactly "len" characters,
truncating or padding on the right with spaces as necessary */
var x = text.length;
if (x > len) {
return text.substring(0, len);
} else {
x = len-x;
while (x-- > 0) {
text += " ";
}
return text;
}
}
function stringToANSI(text, bytes, bx) {
/* Translates the characters in a string to ANSI byte-array format.
"text" is the input string, "bytes" is the Uint8Array output buffer,
and "bx" is the offset into that output buffer */
var len = text.length;
var x;
for (x=0; x<len; x++) {
bytes[bx++] = text.charCodeAt(x) & 0xFF;
}
}
function wordsToANSI(words, wx, wLength, bytes, bx) {
/* Translates an array of B5500 words to ANSI byte-array format.
"words" = the array of words
"wx" = the starting index in "words"
"wLength" = the number of words to translate
"bytes" = a Uint8Array array
"bx" = the starting index in "bytes" to store the translated data */
var c;
var w;
var x;
var y;
var z;
for (x=0; x<wLength; x++) {
w = words[x+wx] || 0;
for (y=0; y<8; y++) {
z = w % 0x40000000000;
c = (w-z)/0x40000000000;
bytes[bx++] = BICtoANSI[c].charCodeAt(0);
w = z*64;
}
}
}
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 lab2;
var mfid;
var rec;
var w;
var x;
lab = readTapeLabel(ctl);
if (ctl.eof) {
spout("TapeDir: EOF encountered when tape label expected, block=" + ctl.blockCount);
} else if (!lab.isLabel) {
spout(lab.text);
spout("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) {
spout("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) {
spout("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) {
spout("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);
lab2 = readTapeLabel(ctl);
if (!lab2.isLabel) {
spout("TapeDir: Tape label expected after directory, block=" + ctl.blockCount);
} else if (lab2.mfid != lab.mfid || lab2.fid != lab.fid) {
spout("TapeDir: Directory ending label mismatch, block=" + ctl.blockCount);
}
}
return dir;
}
function readDiskHeader(ctl) {
/* Reads the next block from the tape blob and (partially) decodes it as a B5500
disk header, returning the header object */
var block;
var header = {
recordLength: 0,
blockLength: 0,
recordsPerBlock: 0,
segmentsPerBlock: 0,
logCreationDate: 0,
logCreationTime: 0,
lastAccessDate: 0,
creationDate: 0,
fileClass: 0,
fileType: 0,
recordCount: 0,
segmentsPerRow: 0,
maxRows: 0,
rowAddress: []};
block = readWordBlock(ctl);
if (ctl.eof) {
spout("DiskHeader: EOF encountered reading header, block=" + ctl.blockCount);
} else if (block.length < 11) {
spout("DiskHeader: header too short, got " + block.length + ", block=" + ctl.blockCount);
} else {
header.recordLength = fieldIsolate(block[0], 0, 15);
header.blockLength = fieldIsolate(block[0], 15, 15);
header.recordsPerBlock = fieldIsolate(block[0], 30, 12);
header.segmentsPerBlock = fieldIsolate(block[0], 42, 6);
header.logCreationDate = fieldIsolate(block[1], 6, 18);
header.logCreationTime = fieldIsolate(block[1], 25, 23);
header.lastAccessDate = fieldIsolate(block[3], 12, 18);
header.creationDate = fieldIsolate(block[3], 30, 18);
header.fileClass = fieldIsolate(block[4], 9, 2);
header.fileType = fieldIsolate(block[4], 36, 6);
header.recordCount = block[7];
header.segmentsPerRow = block[8];
header.maxRows = fieldIsolate(block[9], 43, 5);
header.rowAddress = block.slice(10);
}
return header;
}
function extractFileRow(ctl, header, box, recs) {
/* Extracts the next row from the tape blob and writes it one record at a time to
the "box" textarea object. "recs" is the number of records converted at entry to the
routine. Returns the number of records converted */
var block;
var blockChars = header.blockLength*8;
var blockRecs = 0;
var bx = 0;
var done = false;
var recChars = header.recordLength*8;
var rowRecs = 0;
var rx = 0;
var segs = 0;
var text = "";
var value = "";
// Assemble the row data from tape blocks
do {
block = readTextBlock(ctl);
if (ctl.eof) {
done = true;
} else {
text += block;
segs += Math.floor((block.length+239)/240);
if (segs >= header.segmentsPerRow) {
done = true;
}
}
} while (!done);
// Loop through the file blocks within the row data
while (bx < text.length) {
rx = bx;
blockRecs = header.recordsPerBlock;
while (blockRecs > 0) {
if (rx >= text.length) {
blockRecs = 0;
} else if (recs+rowRecs > header.recordCount) {
blockRecs = 0;
bx = text.length;
} else {
value += (text.substring(rx, rx+recChars) + "\n");
rx += recChars;
rowRecs++;
blockRecs--;
}
}
bx += blockChars;
}
box.value += value;
return rowRecs;
}
function extractFile(ctl, fileNr, fileName) {
/* Extracts the next file in sequence from the tape blob, converts the data
from BIC to ASCII, and writes it to a new window object within the browser.
Returns true if no more files should be converted */
var block;
var box;
var header;
var lab;
var lab2;
var recs = 0;
var result = false;
var rowCount = 0;
var text;
var win;
var x;
spout(" ");
spout("Extracting #" + fileNr + ": " + fileName);
lab = readTapeLabel(ctl);
if (ctl.eof) {
spout("Extract: EOF encountered when tape label expected, block=" + ctl.blockCount);
} else if (!lab.isLabel) {
spout(lab.text);
spout("Extract: Above block encountered when a tape label was expected, block=" + ctl.blockCount);
} else {
block = readWordBlock(ctl);
if (!ctl.eof) {
spout("TapeDir: EOF expected after starting label, block=" + ctl.blockCount);
}
header = readDiskHeader(ctl);
spout(" " + lab.mfid + "/" + lab.fid +
": REC=" + header.recordLength +
", BLK=" + header.blockLength +
", RPB=" + header.recordsPerBlock +
", SPB=" + header.segmentsPerBlock +
", LCD=" + header.logCreationDate +
", LCT=" + header.logCreationTime +
", LAD=" + header.lastAccessDate +
", CRD=" + header.creationDate +
", FCL=" + header.fileClass +
", FTY=" + header.fileType +
", CNT=" + header.recordCount +
", SPR=" + header.segmentsPerRow +
", MXR=" + header.maxRows);
text = " Rows @ [";
for (x=0; x<header.rowAddress.length; x++) {
if (x>0) {
text += ", ";
}
text += header.rowAddress[x].toString(10);
if (header.rowAddress[x] != 0) {
rowCount++;
}
}
spout(text + "], allocated=" + rowCount);
text = "Tape " + rtrim(lab.mfid) + "/" + rtrim(lab.fid) + ": " + fileName;
win = window.open("", lab.fid, "width=800,height=600,status,scrollbars");
win.status = text;
win.moveTo((screen.availWidth - 800)/2, (screen.availHeight - 600)/2);
win.focus();
win.document.body.appendChild(
win.document.createElement("tt").appendChild(
win.document.createTextNode(text)));
win.document.body.appendChild(win.document.createElement("br"));
box = win.document.createElement("textarea");
box.cols = 90;
box.rows = 30;
win.document.body.appendChild(box);
while (!ctl.eof) {
recs += extractFileRow(ctl, header, box, recs);
}
lab2 = readTapeLabel(ctl);
if (!lab2.isLabel) {
spout("Extract: Tape label expected after file data, block=" + ctl.blockCount);
} else if (lab2.mfid != lab.mfid || lab2.fid != lab.fid) {
spout("Extract: File ending label mismatch, block=" + ctl.blockCount);
}
spout(" " + lab2.mfid + "/" + lab2.fid + ": records=" + recs);
box.focus();
box.select();
result = !confirm("Copy and save " + fileName + " from the sub-window.\n" +
"Then click OK to continue or Cancel to quit.");
win.close();
}
return result;
}
function fileLoader_onLoad(ev) {
/* Handle the onload event for an ArrayBuffer FileReader */
var buf = ev.target.result;
var data = new DataView(buf); // use DataView() to avoid problems with littleendians.
var tapeDir;
var text = "";
var x = 0;
clearPanel();
tapeCtl.data = data;
tapeCtl.offset = 0;
tapeCtl.dataLength = buf.byteLength;
tapeCtl.eof = false;
tapeCtl.eot = false;
tapeCtl.blockCount = 0;
tapeDir = readTapeDirectory(tapeCtl);
for (x=0; x<tapeDir.length; x++) {
spout(tapeDir[x]);
}
for (x=1; x<tapeDir.length; x++) {
if (extractFile(tapeCtl, x, tapeDir[x])) {
break;
}
}
}
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();
//alert("File selected: " + f.name +
// "\nModified " + f.lastModifiedDate +
// "\nType=" + f.type + ", Size=" + f.size + " octets");
reader.onload = fileLoader_onLoad;
reader.readAsArrayBuffer(f);
}
function initializeDisk() {
/* Performs a B5500 Cold Start by initializing the directory structure on
the disk, overwriting (and destroying) whatever else was there before */
var buffer = new Uint8Array(240);
var eu;
var euName = euPrefix + "0";
var fileLabels = new Uint8Array(240);
var fileNr = 0;
var info = [];
var segNr;
var shar = [];
var txn;
var zeroes = new Uint8Array(240);
var x;
function loadBootstrap(eu, buffer) {
/* Creates the Halt/Load Button Card image and stores it in segment 1 */
var w = [];
w[ 0] = parseInt("0441341003604231", 8);
w[ 1] = parseInt("7500000000000023", 8);
w[ 2] = parseInt("0211001441310435", 8);
w[ 3] = parseInt("7012700704210014", 8);
w[ 4] = parseInt("4411005441314155", 8);
w[ 5] = parseInt("6461106500000425", 8);
w[ 6] = parseInt("0074013100644131", 8);
w[ 7] = parseInt("0000006200644131", 8);
w[ 8] = parseInt("0000006601044131", 8);
w[ 9] = parseInt("0000007201244131", 8);
w[10] = parseInt("0000007601444131", 8);
w[11] = parseInt("5140000040700137", 8);
w[12] = parseInt("5140000047700461", 8);
w[13] = parseInt("5140000047704223", 8);
w[14] = parseInt("7700000000000037", 8);
w[15] = parseInt("0153020404050000", 8);
w[16] = parseInt("0167010604410440", 8);
w[17] = parseInt("0163010604410010", 8);
w[18] = parseInt("0157010604410660", 8);
w[19] = parseInt("0600017205204131", 8);
wordsToANSI(w, 0, 30, buffer, 0);
eu.put(buffer, 1);
}
function enterFile(mfid, fid, areas, areasize, eu, buffer, directoryTop, labels, fileNr, segNr) {
/* Enters a file into the disk directory. The loader will only create
one directory block, so do not call this routine more than 15 times.
Only the first row is allocated. Returns the next available segment address */
var header = [];
var labelx = 240-(fileNr+1)*16;
stringToANSI(padToLength(mfid, 7), labels, labelx+1);
stringToANSI(padToLength(fid, 7), labels, labelx+9);
if (labelx > 15) {
stringToANSI("0000001?", labels, labelx-16); // @114, last-entry marker
stringToANSI("00000000", labels, labelx-8);
}
header[0] = 0x41; // BIC "11" = @0101 = 1 rec/block, 1 seg/block
header[3] = 0x1001200; // Date: BIC "00010180" = 1980-01-01
header[7] = areas*areasize-1;
header[8] = areasize;
header[9] = areas;
header[10] = segNr;
wordsToANSI(header, 0, 30, buffer, 0);
eu.put(buffer, directoryTop + 18 - fileNr);
return segNr + areasize;
}
/***** Start of initializeDisk *****/
wordsToANSI(shar, 0, 30, zeroes, 0); // create a segment buffer of zeroes
wordsToANSI(shar, 0, 30, fileLabels, 0); // initialize the file name label block
// Start a transaction
txn = disk.transaction([euName, "CONFIG"], "readwrite");
txn.oncomplete = function(ev) {
alert("Cold Start completed successfully");
};
eu = txn.objectStore(euName);
txn.objectStore("CONFIG").get(0).onsuccess = function(ev) {
config = ev.target.result;
if (!config) {
alert("No CONFIG object in database");
txn.abort();
} else {
// Initialize the directory labels segment
stringToANSI("0000001?", fileLabels, 14*16); // @114, last-entry marker
stringToANSI("00000000", fileLabels, 14*16+8);
// Initialize segment 0
shar[ 0] = 1; // number of shared-disk systems
shar[ 1] = directoryTop;
shar[ 2] = 0;
shar[ 3] = 0;
shar[ 4] = directoryEnd; // DIRECT deck option
wordsToANSI(shar, 0, 30, buffer, 0);
eu.put(buffer, 0);
// Load the Halt Load Button Card image
loadBootstrap(eu, buffer); // load the Halt/Load Button Card image
// Clean out the ESPDISK area
for (x=50; x<directoryTop; x++) {
eu.put(zeroes, x);
}
// Initialize the DIRECTORYTOP segment
info[ 0] = // option word
// 47: use DRA
// 46: use DRB
pow2[47-45] + // 45: print BOJ
pow2[47-44] + // 44: print EOJ
pow2[47-43] + // 43: type file open
pow2[47-42] + // 42: call TERMINATE procedure
pow2[47-41] + // 41: initialize date @ H/L
pow2[47-40] + // 40: initialize time @ H/L
// 39: use only one breakout tape
// 38: automatically print pbt
pow2[47-37] + // 37: clear write ready status @ terminal
pow2[47-36] + // 36: write disc. code on terminal
pow2[47-35] + // 35: type when compiler files open & close
pow2[47-34] + // 34: type file close
pow2[47-33] + // 33: error msgs when progr recovery used
pow2[47-32] + // 32: type MT retention messages
pow2[47-31] + // 31: type library messages
pow2[47-30] + // 30: type schedule messages
pow2[47-29] + // 29: type file security messages
pow2[47-28] + // 28: prevent I/O below user disk area
pow2[47-27] + // 27: prevent disk RELEASE statement
pow2[47-26] + // 26: printer backup disk release
pow2[47-25] + // 25: check memory links
pow2[47-24] + // 24: type disk error messages
pow2[47-23] + // 23: disk logging
pow2[47-22] + // 22: suppress library error messages
pow2[47-21] + // 21: go to printer back-up only
pow2[47-20] + // 20: dont stack files on PB tapes
pow2[47-19] + // 19: print set or reset messages
// 18: no user disk will unload expired
pow2[47-17] + // 17: run all decks(SHAREDISK)
// 16: olay core to ECM(AUXMEM)
pow2[47-15] + // 15: job core estimates(STATISTICS)
// 14: olay data to ECM(AUXMEM)
pow2[47-13] + // 13: makes system hang on-should HL msg
// 12: enables datacom(TSS, if not DCP)
pow2[47-11] + // 11: library messages for CANDE
pow2[47-10] + // 10: ZIP decks to run on batch(SHAREDISK)
// 9: controls running of batch jobs on TSS
// 8: UNUSED
// 7: UNUSED
// 6: UNUSED
// 5: UNUSED
// 4: UNUSED
// 3: UNUSED
pow2[47- 2] + // 2: Model III I/O channels
// 1: UNUSED
0; // 0: (flag bit)
info[ 1] = 0x1001200; // Date: BIC "00010180" = 1980-01-01
info[ 2] = config.eus; // number of EUs
info[ 3] = 0; // not used
info[ 4] = directoryEnd; // DIRECT deck option
info[ 5] = 0; // last number used for control deck
info[ 6] = 0; // first control deck queued
info[ 7] = 0; // last control deck queued
info[ 8] = 0; // next number available for printer backup disk
info[ 9] = 1; // multiprocessing core factor
info[10] = 0; // SPO stations (through info[15])
info[11] = 0;
info[12] = 0;
info[13] = 0;
info[14] = 0;
info[15] = 0;
info[16] = 15; // Q value for datacom input
wordsToANSI(info, 0, 30, buffer, 0);
eu.put(buffer, directoryTop);
// Create a file entry for the system log
segNr = directoryEnd + 1;
segNr = enterFile("SYSTEM", "LOG", 1, 20000, eu, buffer, directoryTop, fileLabels, fileNr, segNr);
fileNr++;
// Store the directory labels segment
eu.put(fileLabels, directoryTop + 19); // write the directory block file labels
}
};
}
function configureDatabase(ev) {
/* Handles the onupgradeneeded event for the database */
var configStore = null;
var disk = ev.target.result;
var stores = disk.objectStoreNames;
function configureEUs(configStore, config) {
var euName;
// Note: for now we will not worry about shrinking or deleting EUs that
// are in conflict with the contents of euSet.
for (euName in euSet) {
if (euName.indexOf("EU") == 0) {
if (!config[euName]) {
config[euName] = euSet[euName];
}
if (stores.contains(euName)) {
if (euSet[euName] > config[euName]) {
config[euName] = euSet[euName];
}
} else {
config[euName] = euSet[euName];
config.eus++;
disk.createObjectStore(euName);
}
}
}
configStore.put(config, 0);
}
if (stores.contains(configName)) {
configStore = disk.transaction(configName).objectStore(configName);
configStore.get(0).onsuccess = function(ev) {
config = ev.target.result || {eus:0};
configureEUs(configStore, config);
};
} else {
config = {eus:0};
configStore = disk.createObjectStore(configName);
configureEUs(configStore, config);
}
}
function genericDBError(ev) {
/* Formats a generic alert when otherwise-unhandled database errors occur */
var disk = ev.currentTarget.result;
alert("Database \"" + disk.name + "\" error: " + ev.target.result.error);
}
function dumpDisk() {
/* Dumps the initial and directory portions of the disk */
var txn = disk.transaction("EU0");
var eu = txn.objectStore("EU0");
var range = IDBKeyRange.upperBound(directoryTop+20);
var req = eu.openCursor(range);
var lastKey = -1;
spout("===== START OF DISK DUMP =====");
req.onsuccess = function(ev) {
var cursor = ev.target.result;
if (cursor) {
if (cursor.key-lastKey > 1) {
spout("----- " + (cursor.key-lastKey-1) + " unallocated segments -----");
}
spout(cursor.key + ": " + String.fromCharCode.apply(null, cursor.value));
lastKey = cursor.key;
cursor.continue();
} else {
spout("===== END OF DISK DUMP =====");
}
};
}
function openDatabase(name, version) {
/* Attempts to open the disk subsystem database for the specified "name"
and "version". Returns the IDB database object if successful, or null if
unsuccessful */
var db = null;
var req;
req = window.indexedDB.open(name, version);
req.onerror = function(ev) {
alert("Cannot open disk database: " + ev.target.error);
};
req.onblocked = function(ev) {
alert("Database.open is blocked -- cannot continue");
};
req.onsuccess = function(ev) {
disk = ev.target.result; // save the object reference globally for later use
alert("Disk database opened: " + name + " #" + disk.version);
disk.onerror = genericDBError;
$$("ColdstartBtn").disabled = false;
dumpDisk(); // <<<<<<<<<<<<<< DEBUG <<<<<<<<<<<<<<<<<
};
req.onupgradeneeded = configureDatabase;
return db;
}
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 (!window.indexedDB) {missing += ", IndexedDB"}
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()) {
$$("FileSelector").addEventListener("change", fileSelector_onChange, false);
$$("ColdstartBtn").addEventListener("click", function(ev) {
if (confirm("Are you sure you want to do a COLD START?")) {
initializeDisk();
}
}, false);
openDatabase(dbName, dbVersion);
}
}
</script>
</head>
<body>
<div style="position:relative; width:100%; height:3em">
<div style="position:absolute; left:0; top:0; width:auto">
retro-B5500 Coldstart Disk SubSystem Loader
</div>
<div style="text-align:center">
<input id=ColdstartBtn type=button DISABLED value="Cold Start">
</div>
<div style="position:absolute; top:0; right:0; width:auto">
<input id=FileSelector type=file size=60>
</div>
</div>
<pre id=TextPanel>
</pre>
</body>
</html>