mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-02-13 11:44:33 +00:00
1. Implement new system and disk subsystem configuration mechanism. 2. Implement initial Mark-XIII Cold Start card deck for use with new configuration interfaces. 3. Deprecate use of B5500ColdLoader.html script (replaced by new configuration mechanism and Cold Start deck), but correct and enhance IndexedDB database detection, creation, and deletion in it. 4. Implement "Application Cache" support to allow emulator to run off-line in a browser. 5. Implement web-font support and update all UIs to use DejaVu Sans and DejaVu Sans Mono from downloaded .woff or .ttf font files. 6. Rework some code in Processor OPDC, DESC, and indexDescriptor routines, attempting to resolve Flag Bit errors (issue #23). This appears to result in some improvement, but we still see them occasionally under load. 7. Line Printer: - Implement new line printer driver with more realistic UI and operator controls. - Implement Algol Glyphs option to render special Algol characters in Unicode. - Implement support for optional "greenbar" shading on the "paper". 8. SPO: - Redesign SPO driver to accept input from a text <input> element instead of capturing keystrokes directly from the window or "paper" <iframe>. This was done to allow input from tablet and mobile devices that will not pop up a keyboard unless an input-like element has the focus. - Implement Unicode Algol Glyphs support on output. - Intelligently resize "paper" area when SPO window is resized. - Accept "_" as a substitute for "~" as end-of-message on input. 9. Card Punch: - Implement Unicode Algol Glyphs support on output. - Implement stacker-full annunciators in UI. 10. Card Reader: - Implement Unicode Algol Glyphs support on input. - Accept "_" as a substitute for "~" on input. 11. Disk: - Adapt B5500DiskUnit driver to new configuration mechanism. - Implement support for Model-IB (slow) disk and non-DFX disk storage configurations; support up to 20 EUs. - Implement check for DKA readiness in cc.load() if not doing card-load-select. 12. Datacom: - Rework datacom driver keystroke handling for compatibility with Google Chrome. - Correct typo (line 437) in B5500DatacomUnit reported by Peter Grootswagers (issue #28). 13. Magnetic Tape: - Implement more granular tape reel animation in B5500MagTapeDrive. - Open the tape loader window on top of its device window. 14. Correct color of NOT READY lamps in peripheral device UIs; convert <progress> bars to <meter> elements. 15. More intelligently resize peripheral UI controls when their window is resized. 16. Implement lamp test during power-on in B5500Console. 17. Illuminate NOT READY light on Console at power-on if certain minimum configuration requirements are not met. 18. Set all HTML <meta> Content-Type character sets to UTF-8 (were ISO-8859-1); correct problem with FireFox requiring the character set to be specified within the first 1024 characters of an HTML file. 19. Clean up and refactor CSS style sheets 20. Split Javascript code out from B5500Console.html to new B5500Console.js. 21. Refactor common UI routines into webUI\B5500Util.js. 22. Move images and fonts to new webUI/resources directory; rearrange files in webUI/tool, tools, tests, source directories of repo. 23. Make significant wiki updates to document the new features in this release.
598 lines
25 KiB
HTML
598 lines
25 KiB
HTML
<!DOCTYPE html>
|
|
<head>
|
|
<title>B5500 Disk SYSTEM/LOG Fixer Utility</title>
|
|
<meta name="Author" content="Paul Kimpel">
|
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
<meta http-equiv="Content-Script-Type" content="text/javascript">
|
|
<meta http-equiv="Content-Style-Type" content="text/css">
|
|
|
|
<script>
|
|
/***********************************************************************
|
|
* retro-b5500/tools B5500DiskSystemLogFixer.html
|
|
************************************************************************
|
|
* Copyright (c) 2013, Paul Kimpel.
|
|
* Licensed under the MIT License,
|
|
* see http://www.opensource.org/licenses/mit-license.php
|
|
************************************************************************
|
|
* B5500 Disk SYSTEM/LOG Fixer Utility.
|
|
*
|
|
* This script opens an IndexedDB database in the browser and attempts to
|
|
* treat it as a B5500 disk image, finding a specified file in the disk
|
|
* directory structure, manipulating the header, and writing the header
|
|
* back to the directory. This is intended to be a customized one-timer
|
|
* sort of program, modified as necessary for the fix-up task at hand.
|
|
************************************************************************
|
|
* 2013-07-27 P.Kimpel
|
|
* Original version, from B5500DiskDirList.html.
|
|
***********************************************************************/
|
|
"use strict";
|
|
|
|
if (!window.indexedDB) { // for Safari, mostly
|
|
window.indexedDB = window.webkitIndexedDB || window.mozIndexedDB;
|
|
}
|
|
|
|
window.addEventListener("load", function() {
|
|
var configName = "CONFIG"; // database configuration store name
|
|
var dbName = "B5500DiskUnit"; // IDB database name
|
|
var dbVersion; // current IDB database version (leave undefined)
|
|
var directoryTop; // start of directory area
|
|
var directoryEnd; // end of directory area
|
|
var euPrefix = "EU"; // prefix for EU object store names
|
|
|
|
var config = null; // copy of CONFIG store contents
|
|
var disk = null; // the IDB database object
|
|
var panel = $$("TextPanel");
|
|
|
|
var BICtoANSI = [ // Index by 6-bit BIC to get 8-bit ANSI code
|
|
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37, // 00-07, @00-07
|
|
0x38,0x39,0x23,0x40,0x3F,0x3A,0x3E,0x7D, // 08-1F, @10-17
|
|
0x2B,0x41,0x42,0x43,0x44,0x45,0x46,0x47, // 10-17, @20-27
|
|
0x48,0x49,0x2E,0x5B,0x26,0x28,0x3C,0x7E, // 18-1F, @30-37
|
|
0x7C,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50, // 20-27, @40-47
|
|
0x51,0x52,0x24,0x2A,0x2D,0x29,0x3B,0x7B, // 28-2F, @50-57
|
|
0x20,0x2F,0x53,0x54,0x55,0x56,0x57,0x58, // 30-37, @60-67
|
|
0x59,0x5A,0x2C,0x25,0x21,0x3D,0x5D,0x22]; // 38-3F, @70-77
|
|
|
|
var BICtoBCLANSI = [ // Index by 6-bit BIC to get 8-bit BCL-as-ANSI code
|
|
0x23,0x31,0x32,0x33,0x34,0x35,0x36,0x37, // 00-07, @00-07
|
|
0x38,0x39,0x40,0x3F,0x30,0x3A,0x3E,0x7D, // 08-1F, @10-17
|
|
0x2C,0x2F,0x53,0x54,0x55,0x56,0x57,0x58, // 10-17, @20-27
|
|
0x59,0x5A,0x25,0x21,0x20,0x3D,0x5D,0x22, // 18-1F, @30-37
|
|
0x24,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,0x50, // 20-27, @40-47
|
|
0x51,0x52,0x2A,0x2D,0x7C,0x29,0x3B,0x7B, // 28-2F, @50-57
|
|
0x2B,0x41,0x42,0x43,0x44,0x45,0x46,0x47, // 30-37, @60-67
|
|
0x48,0x49,0x5B,0x26,0x2E,0x28,0x3C,0x7E]; // 38-3F, @70-77
|
|
|
|
var ANSItoBIC = [ // Index by 8-bit ANSI to get 6-bit BIC (upcased, invalid=>"?")
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // 00-0F
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // 10-1F
|
|
0x30,0x3C,0x3F,0x0A,0x2A,0x3B,0x1C,0x0C,0x1D,0x2D,0x2B,0x10,0x3A,0x2C,0x1A,0x31, // 20-2F
|
|
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0D,0x2E,0x1E,0x3D,0x0E,0x0C, // 30-3F
|
|
0x0B,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x21,0x22,0x23,0x24,0x25,0x26, // 40-4F
|
|
0x27,0x28,0x29,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x1B,0x0C,0x3E,0x0C,0x0C, // 50-5F
|
|
0x0C,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x21,0x22,0x23,0x24,0x25,0x26, // 60-6F
|
|
0x27,0x28,0x29,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x2F,0x20,0x0F,0x1F,0x0C, // 70-7F
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // 80-8F
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // 90-9F
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // A0-AF
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // B0-BF
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // C0-CF
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // D0-DF
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // E0-EF
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C]; // F0-FF
|
|
|
|
var BCLANSItoBIC = [ // Index by 8-bit BCL-as-ANSI to get 6-bit BIC (upcased, invalid=>"?")
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // 00-0F
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // 10-1F
|
|
0x1C,0x1B,0x1F,0x00,0x20,0x1A,0x3B,0x0C,0x3D,0x2D,0x2A,0x30,0x10,0x2B,0x3C,0x11, // 20-2F
|
|
0x0C,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0D,0x2E,0x3E,0x1D,0x0E,0x0B, // 30-3F
|
|
0x0A,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x21,0x22,0x23,0x24,0x25,0x26, // 40-4F
|
|
0x27,0x28,0x29,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x3A,0x0C,0x1E,0x0C,0x0C, // 50-5F
|
|
0x0C,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x21,0x22,0x23,0x24,0x25,0x26, // 60-6F
|
|
0x27,0x28,0x29,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x2F,0x2C,0x0F,0x3F,0x0C, // 70-7F
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // 80-8F
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // 90-9F
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // A0-AF
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // B0-BF
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // C0-CF
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // D0-DF
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C, // E0-EF
|
|
0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C]; // F0-FF
|
|
|
|
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 bitTest(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);
|
|
$$("PageBottom").scrollIntoView();
|
|
}
|
|
|
|
/**************************************/
|
|
function clearPanel() {
|
|
/* Clears the text panel */
|
|
var kid;
|
|
|
|
while (kid = panel.firstChild) {
|
|
panel.removeChild(kid);
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
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, asBinary) {
|
|
/* Translates the characters in a string to upper case, and then 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. If "asBinary" is
|
|
truthy, the translation is binary, otherwise it is done as BCLANSI */
|
|
var len = text.length;
|
|
var table1 = (asBinary ? BICtoANSI : BICtoBCLANSI);
|
|
var utxt = text.toUpperCase();
|
|
var x;
|
|
|
|
bx = bx || 0;
|
|
for (x=0; x<len; x++) {
|
|
bytes[bx++] = table1[ANSItoBIC[utxt.charCodeAt(x) & 0xFF]];
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
function wordsToANSI(words, wx, wLength, bytes, bx, asBinary) {
|
|
/* 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
|
|
"asBinary" = if truthy, then binary translation is done; otherwise
|
|
B5500 BCLANSI translation is done */
|
|
var c;
|
|
var table = (asBinary ? BICtoANSI : BICtoBCLANSI);
|
|
var w;
|
|
var x;
|
|
var y;
|
|
var z;
|
|
|
|
bx = bx || 0;
|
|
if (wLength < 0) {
|
|
wLength = -wLength;
|
|
}
|
|
for (x=0; x<wLength; x++) {
|
|
w = words[wx+x] || 0;
|
|
for (y=0; y<8; y++) {
|
|
z = w % 0x40000000000;
|
|
c = (w-z)/0x40000000000;
|
|
bytes[bx++] = table[c];
|
|
w = z*64;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
function wordsToString(words, wx, wLength, asBinary) {
|
|
/* Translates an array of B5500 words to a string and returns the string.
|
|
"words" = the array of words
|
|
"wx" = the starting index in "words"
|
|
"wLength" = the number of words to translate
|
|
"asBinary" = if truthy, then binary translation is done; otherwise
|
|
B5500 BCLANSI translation is done */
|
|
var c;
|
|
var table = (asBinary ? BICtoANSI : BICtoBCLANSI);
|
|
var text = "";
|
|
var w;
|
|
var x;
|
|
var y;
|
|
var z;
|
|
|
|
if (wLength < 0) {
|
|
wLength = -wLength;
|
|
}
|
|
for (x=0; x<wLength; x++) {
|
|
w = words[wx+x] || 0;
|
|
for (y=0; y<8; y++) {
|
|
z = w % 0x40000000000;
|
|
c = (w-z)/0x40000000000;
|
|
text += String.fromCharCode(table[c]);
|
|
w = z*64;
|
|
}
|
|
}
|
|
return text;
|
|
}
|
|
|
|
/**************************************/
|
|
function ANSItoWords(bytes, bx, bLength, words, wx, asBinary) {
|
|
/* Translates a portion of an ANSI byte array to a sequence of B5500 words.
|
|
"bytes" = the Uint8Array byte array
|
|
"bx" = 0-relative offset into "bytes"
|
|
"bLength" = number of bytes to translate
|
|
"words" = the word array
|
|
"wx" = 0-relative offset into "words" to store the translated data
|
|
"asBinary" = if truthy, then binary translation is done; otherwise
|
|
B5500 BCLANSI translation is done */
|
|
var cx = 0;
|
|
var w = 0;
|
|
var table = (asBinary ? ANSItoBIC : BCLANSItoBIC);
|
|
var x;
|
|
|
|
wx = wx || 0;
|
|
if (bLength < 0) {
|
|
bLength = -bLength;
|
|
}
|
|
for (x=0; x<bLength; x++) {
|
|
if (cx >= 8) {
|
|
words[wx++] = w;
|
|
w = cx = 0;
|
|
}
|
|
w = w*64 + table[bytes[bx+x]];
|
|
cx++;
|
|
}
|
|
while (cx++ < 8) {
|
|
w *= 64;
|
|
}
|
|
words[wx++] = w;
|
|
}
|
|
|
|
/**************************************/
|
|
function readDiskBlock(addr, segs, block, callback) {
|
|
/* Reads a block from the disk "eu" at "addr" for "segs" segments, translates
|
|
it to words in the "block" array, then calls "callback" passing the address
|
|
and block */
|
|
var bx = 0;
|
|
var eu;
|
|
var euAddr = addr % 1000000;
|
|
var euNr = (addr % 10000000 - euAddr)/1000000;
|
|
var euName = euPrefix + euNr.toString();
|
|
var endAddr = euAddr + segs - 1;
|
|
var nextAddr = euAddr;
|
|
var range = IDBKeyRange.bound(euAddr, endAddr);
|
|
var req;
|
|
var txn;
|
|
var x;
|
|
|
|
txn = disk.transaction(euName);
|
|
eu = txn.objectStore(euName);
|
|
req = eu.openCursor(range);
|
|
|
|
req.onsuccess = function(ev) {
|
|
var cursor = ev.target.result;
|
|
|
|
if (cursor) {
|
|
while (cursor.key > nextAddr) {
|
|
for (x=0; x<30; x++) {
|
|
block[bx++] = 0;
|
|
}
|
|
nextAddr++;
|
|
}
|
|
ANSItoWords(cursor.value, 0, 240, block, bx);
|
|
bx += 30;
|
|
nextAddr++;
|
|
cursor.continue();
|
|
} else {
|
|
while (nextAddr <= endAddr) {
|
|
for (x=0; x<30; x++) {
|
|
block[bx++] = 0;
|
|
}
|
|
nextAddr++;
|
|
}
|
|
callback(addr, block);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**************************************/
|
|
function normalizeName(name) {
|
|
/* Normalizes the name string to match the way file names are stored
|
|
in the B5500 directory, i.e., with a leading zero and trailing spaces
|
|
to a length of 8 */
|
|
var s = "0" + name;
|
|
|
|
if (s.length > 8) {
|
|
s = s.substring(0, 8);
|
|
} else {
|
|
while (s.length < 8) {
|
|
s += " ";
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/**************************************/
|
|
function readDiskHeader(block) {
|
|
/* Decodes "block" as a B5500 disk header, returning the header object */
|
|
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: [],
|
|
words: []};
|
|
|
|
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);
|
|
header.words = block; // save the raw header words
|
|
|
|
return header;
|
|
}
|
|
|
|
/**************************************/
|
|
function fixIt(mfid, fid, headerAddr, headerBlock) {
|
|
/* Callback (successor) function to fix up a disk header */
|
|
/* This version corrects the record count in SYSTEM/LOG */
|
|
var buffer = new Uint8Array(240);
|
|
var eu;
|
|
var euName = euPrefix + "0";
|
|
var h = readDiskHeader(headerBlock);
|
|
var txn;
|
|
|
|
headerBlock[7] = h.segmentsPerRow*h.recordsPerBlock/h.segmentsPerBlock - 1;
|
|
|
|
txn = disk.transaction([euName], "readwrite");
|
|
txn.oncomplete = function(ev) {
|
|
alert(mfid + "/" + fid + " fix-up completed successfully");
|
|
};
|
|
|
|
eu = txn.objectStore(euName);
|
|
wordsToANSI(headerBlock, 0, 30, buffer, 0);
|
|
eu.put(buffer, headerAddr);
|
|
}
|
|
|
|
/**************************************/
|
|
function directorySearch(mfid, fid, successor) {
|
|
/* Reads the existing directory structure to search for the specified
|
|
file named mfid/fid. If found, calls the successor function, passing the
|
|
names, disk header address, and disk header block */
|
|
var block = new Array(480); // directory block (16 segments)
|
|
var f1; // normalized mfid
|
|
var f2; // normalized fid
|
|
|
|
function searchDirBlock(addr, block) {
|
|
/* Lists the entries in the current block; if not the last block,
|
|
advances to the next block until the file names are found */
|
|
var atEnd = false; // searched to end of directory
|
|
var bx; // header block index
|
|
var found = false; // true if requested file found
|
|
var headerBlock; // disk header as Array(30)
|
|
var headerAddr; // segment address of header
|
|
var n1; // current mfid
|
|
var n2; // current fid
|
|
var namex; // current index in name segment (0-14)
|
|
|
|
// Step through the file name entries backwards
|
|
for (namex=14; namex>=0; namex--) {
|
|
bx = namex*2 + 450;
|
|
if (block[bx] == 0x4C) { // 0x4C=@114, end-of-directory marker
|
|
atEnd = true;
|
|
break;
|
|
} else if (block[bx] != 0x0C) { // 0x0C=@14, available directory slot
|
|
// Got a live one -- check its names
|
|
n1 = wordsToString(block, bx, 1, true);
|
|
n2 = wordsToString(block, bx+1, 1, true);
|
|
if (f1==n1 && f2==n2) {
|
|
found = true;
|
|
headerAddr = addr + namex;
|
|
bx = namex*30;
|
|
headerBlock = block.slice(bx, bx+30);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (found) {
|
|
successor(mfid, fid, headerAddr, headerBlock);
|
|
} else if (atEnd) {
|
|
spout("File " + mfid + "/" + fid + " not found.");
|
|
} else {
|
|
readDiskBlock(addr+16, 16, block, searchDirBlock);
|
|
}
|
|
|
|
}
|
|
|
|
/***** outer block of directorySearch *****/
|
|
|
|
f1 = normalizeName(mfid);
|
|
f2 = normalizeName(fid);
|
|
if (!config.EU0) {
|
|
alert("No EU0 in disk configuration -- cannot load");
|
|
} else {
|
|
readDiskBlock(directoryTop+4, 16, block, searchDirBlock);
|
|
}
|
|
}
|
|
|
|
/**************************************/
|
|
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 openDatabase(name, version, successor) {
|
|
/* Attempts to open the disk subsystem database for the specified "name"
|
|
and "version". Stores the IDB database object in "disk" if successful, or
|
|
stores null if unsuccessful. Also gets directoryTop from seg 0 */
|
|
var block = new Array(30);
|
|
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
|
|
disk.onerror = genericDBError;
|
|
// alert("Disk database opened: " + name + " #" + disk.version);
|
|
|
|
disk.transaction("CONFIG").objectStore("CONFIG").get(0).onsuccess = function(ev) {
|
|
config = ev.target.result;
|
|
readDiskBlock(0, 1, block, function(addr, block) {
|
|
directoryTop = block[1];
|
|
directoryEnd = block[4];
|
|
successor();
|
|
});
|
|
};
|
|
};
|
|
|
|
}
|
|
|
|
/**************************************/
|
|
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()) {
|
|
openDatabase(dbName, dbVersion, function() {
|
|
directorySearch("SYSTEM", "LOG", fixIt);
|
|
});
|
|
}
|
|
}, false);
|
|
</script>
|
|
|
|
<style>
|
|
BODY {
|
|
font-family: Arial, Helvetica, sans-serif;
|
|
font-size: small}
|
|
TABLE {
|
|
border-collapse: collapse}
|
|
TH {
|
|
vertical-align: bottom}
|
|
.center {
|
|
text-align: center}
|
|
.rj {
|
|
text-align: right}
|
|
.mono {
|
|
font-family: Courier New, Courier, monospace}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div style="position:relative; width:100%; height:3em">
|
|
<div style="position:absolute; left:0; top:0; width:auto">
|
|
<img src="../webUI/resources/retro-B5500-Logo.png" alt="retro-B5500 Logo" style="float:left">
|
|
Disk SYSTEM/LOG Fixer Utility
|
|
</div>
|
|
</div>
|
|
|
|
<pre id=TextPanel>
|
|
</pre>
|
|
|
|
<div id=PageBottom>
|
|
</div>
|
|
|
|
</body>
|
|
</html> |