mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-04-17 17:52:27 +00:00
1. Release emulator version 0.12.
2. Debug Processor for multi-processor operation (not working yet). 3. Further refinements to Processor.schedule() and throttling management. 4. Correct Processor fetch of Initiate Control Word for IP1 and IP2. 5. Enable second Disk File Control Unit (DKB) -- works. 6. Fix minor P1 bug in cc.clearInterrupt(). 7. Reset file selector control in B5500CardReader to allow same deck to be reloaded under Google Chrome. 8. Fix (finally) creation of SYSTEM/LOG and DIRCTRY/DISK in B5500ColdLoader. 9. Implement tools/B5500DiskDirFixer.html and tools/B5500DiskSystemLogFixer.html utilities to correct bad directory entries due to improper log creation in B5500ColdLoader. 10. Correct tools/LOG-MAKER.job to create SYSTEM/LOG on systems that do not have one. 11. Do not output zero-length lines (mem inhibit) with zero spacing in B5500DummyPrinter. 12. Implement separate setTimeout() tokens for input and output in B5500SPOUnit. 13. Additional de-anonymization of functions.
This commit is contained in:
@@ -61,7 +61,7 @@ function B5500CentralControl() {
|
||||
/**************************************/
|
||||
/* Global constants */
|
||||
|
||||
B5500CentralControl.version = "0.11";
|
||||
B5500CentralControl.version = "0.12";
|
||||
|
||||
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)
|
||||
@@ -157,7 +157,7 @@ B5500CentralControl.bindMethod = function bindMethod(f, context) {
|
||||
Note that this is a constructor property function, NOT an instance method of
|
||||
the CC object */
|
||||
|
||||
return function() {f.apply(context, arguments)};
|
||||
return function bindMethodAnon() {f.apply(context, arguments)};
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -534,7 +534,7 @@ B5500CentralControl.prototype.clearInterrupt = function clearInterrupt() {
|
||||
p1.I &= 0xFD;
|
||||
break;
|
||||
case 0x32: // @62: P1 stack overflow
|
||||
p1.I &= 0x0B;
|
||||
p1.I &= 0xFB;
|
||||
break;
|
||||
case 0x34: // @64-75: P1 syllable-dependent
|
||||
case 0x35:
|
||||
@@ -790,7 +790,7 @@ B5500CentralControl.prototype.load = function load(dontStart) {
|
||||
only the MCP bootstrap is loaded into memory -- P1 is not started */
|
||||
var result;
|
||||
var boundLoadComplete = (function boundLoadComplete(that, dontStart) {
|
||||
return function() {return that.loadComplete(dontStart)}
|
||||
return function boundLoadCompleteAnon() {return that.loadComplete(dontStart)}
|
||||
})(this, dontStart);
|
||||
|
||||
if (!this.P1 || this.P1.busy) { // P1 is busy or not available
|
||||
@@ -1050,7 +1050,7 @@ B5500CentralControl.prototype.powerOff = function powerOff() {
|
||||
if (this.poweredUp) {
|
||||
this.halt();
|
||||
// Wait a little while for I/Os, etc., to finish
|
||||
setTimeout(function() {
|
||||
setTimeout(function powerOffAnon() {
|
||||
shutDown.call(that);
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
@@ -556,7 +556,7 @@ B5500IOUnit.prototype.makeFinish = function makeFinish(f) {
|
||||
/* Utility function to create a closure for I/O finish handlers */
|
||||
var that = this;
|
||||
|
||||
return function(mask, length) {return f.call(that, mask, length)};
|
||||
return function makeFinishAnon(mask, length) {return f.call(that, mask, length)};
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
|
||||
@@ -34,7 +34,8 @@ function B5500Processor(procID, cc) {
|
||||
|
||||
this.processorID = procID; // Processor ID ("A" or "B")
|
||||
this.cc = cc; // Reference back to Central Control module
|
||||
this.scheduler = null; // Reference to current setTimeout id
|
||||
this.schedImmediate = null; // Reference to current setImmediate token
|
||||
this.schedTimeout = null; // Reference to current setTimeout token
|
||||
this.accessor = { // Memory access control block
|
||||
requestorID: procID, // Memory requestor ID
|
||||
addr: 0, // Memory address
|
||||
@@ -57,7 +58,7 @@ function B5500Processor(procID, cc) {
|
||||
/**************************************/
|
||||
|
||||
B5500Processor.timeSlice = 4000; // this.run() timeslice, clocks
|
||||
B5500Processor.delaySamples = 1000; // this.delayThreshold sampling average basis
|
||||
B5500Processor.delaySamples = 1000; // this.delayDeltaAvg sampling average basis
|
||||
|
||||
B5500Processor.collation = [ // index by BIC to get collation value
|
||||
53, 54, 55, 56, 57, 58, 59, 60, // @00: 0 1 2 3 4 5 6 7
|
||||
@@ -113,7 +114,7 @@ B5500Processor.prototype.clear = function clear() {
|
||||
|
||||
this.US14X = 0; // STOP OPERATOR switch
|
||||
|
||||
this.isP1 = this === this.cc.P1; // True if this is the control processor
|
||||
this.isP1 = (this === this.cc.P1); // True if this is the control processor
|
||||
this.busy = 0; // Processor is running, not idle or halted
|
||||
this.controlCycles = 0; // Current control-state cycle count (for UI display)
|
||||
this.cycleCount = 0; // Cycle count for current syllable
|
||||
@@ -137,8 +138,7 @@ B5500Processor.prototype.accessError = function accessError() {
|
||||
this.I |= 0x01; // set I01F: memory parity error
|
||||
this.cc.signalInterrupt();
|
||||
if (this.isP1 && !this.NCSF) {
|
||||
this.busy = 0; // P1 memory parity in control state stops the proc
|
||||
this.cycleLimit = 0; // exit this.run()
|
||||
this.stop(); // P1 memory parity in control state stops the proc
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1310,17 +1310,9 @@ B5500Processor.prototype.storeForInterrupt = function storeForInterrupt(forTest)
|
||||
this.K = 0;
|
||||
this.V = 0;
|
||||
} else {
|
||||
this.T = 0; // idle the processor
|
||||
this.TROF = 0;
|
||||
this.PROF = 0;
|
||||
this.busy = 0;
|
||||
this.cycleLimit = 0; // exit this.run()
|
||||
this.stop(); // idle the processor
|
||||
this.cc.HP2F = 1;
|
||||
this.cc.P2BF = 0; // tell P1 we've stopped
|
||||
if (this.scheduler) {
|
||||
clearTimeout(this.scheduler);
|
||||
this.scheduler = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1349,7 +1341,7 @@ B5500Processor.prototype.start = function start() {
|
||||
this.procTime -= stamp;
|
||||
this.delayLastStamp = stamp;
|
||||
this.delayRequested = 0;
|
||||
setImmediate(this.boundSchedule);
|
||||
this.schedImmediate = setImmediate(this.boundSchedule, 1);
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -1358,13 +1350,17 @@ B5500Processor.prototype.stop = function stop() {
|
||||
var stamp = new Date().getTime();
|
||||
|
||||
this.T = 0;
|
||||
this.TROF = 0;
|
||||
this.TROF = 0; // idle the processor
|
||||
this.PROF = 0;
|
||||
this.busy = 0;
|
||||
this.cycleLimit = 0; // exit this.run()
|
||||
if (this.scheduler) {
|
||||
clearTimeout(this.scheduler);
|
||||
this.scheduler = null;
|
||||
if (this.schedImmediate) {
|
||||
clearImmediate(this.schedImmediate);
|
||||
this.schedImmediate = null;
|
||||
}
|
||||
if (this.schedTimeout) {
|
||||
clearTimeout(this.schedTimeout);
|
||||
this.schedTimeout = null;
|
||||
}
|
||||
while (this.procTime < 0) {
|
||||
this.procTime += stamp;
|
||||
@@ -1374,25 +1370,31 @@ B5500Processor.prototype.stop = function stop() {
|
||||
/**************************************/
|
||||
B5500Processor.prototype.initiate = function initiate(forTest) {
|
||||
/* Initiates the processor from interrupt control words stored in the
|
||||
stack. Assumes the INCW is in A. "forTest" implies use from IFT */
|
||||
var aw = this.A; // local copy of A
|
||||
stack. Assumes the INCW is in TOS. "forTest" implies use from IFT */
|
||||
var bw; // local copy of B
|
||||
var saveAROF = 0;
|
||||
var saveBROF = 0;
|
||||
var temp;
|
||||
|
||||
if (this.AROF) {
|
||||
this.B = bw = this.A;
|
||||
} else {
|
||||
this.adjustBFull();
|
||||
bw = this.B;
|
||||
}
|
||||
|
||||
// restore the Initiate Control Word (INCW) or Initiate Test Control Word
|
||||
this.S = aw % 0x8000;
|
||||
this.CWMF = (aw % 0x10000) >>> 15;
|
||||
this.S = bw % 0x8000;
|
||||
this.CWMF = (bw % 0x10000) >>> 15;
|
||||
if (forTest) {
|
||||
this.TM = (aw % 0x100000 - aw % 0x10000)/0x10000 +
|
||||
(aw % 0x200000 - aw % 0x100000)/0x100000 * 16 + // NCSF
|
||||
(aw % 0x400000 - aw % 0x200000)/0x200000 * 32 + // CCCF
|
||||
(aw % 0x100000000000 - aw % 0x80000000000)/0x80000000000 * 64 + // MWOF
|
||||
(aw % 0x400000000000 - aw % 0x200000000000)/0x200000000000 * 128; // MROF
|
||||
this.Z = (aw % 0x10000000 - aw % 0x400000)/0x400000;
|
||||
this.Y = (aw % 0x400000000 - aw % 0x10000000)/0x10000000;
|
||||
this.Q = (aw % 0x80000000000 - aw % 0x400000000)/0x400000000;
|
||||
this.TM = (bw % 0x100000 - bw % 0x10000)/0x10000 +
|
||||
(bw % 0x200000 - bw % 0x100000)/0x100000 * 16 + // NCSF
|
||||
(bw % 0x400000 - bw % 0x200000)/0x200000 * 32 + // CCCF
|
||||
(bw % 0x100000000000 - bw % 0x80000000000)/0x80000000000 * 64 + // MWOF
|
||||
(bw % 0x400000000000 - bw % 0x200000000000)/0x200000000000 * 128; // MROF
|
||||
this.Z = (bw % 0x10000000 - bw % 0x400000)/0x400000;
|
||||
this.Y = (bw % 0x400000000 - bw % 0x10000000)/0x10000000;
|
||||
this.Q = (bw % 0x80000000000 - bw % 0x400000000)/0x400000000;
|
||||
// Emulator doesn't support J register, so can't set that from TM
|
||||
}
|
||||
|
||||
@@ -1421,7 +1423,10 @@ B5500Processor.prototype.initiate = function initiate(forTest) {
|
||||
this.MSFF = (bw % 0x100000000 - bw % 0x80000000)/0x80000000;
|
||||
this.R = (bw % 0x40000000000 - bw % 0x200000000)/0x200000000;
|
||||
|
||||
if (this.CWMF || forTest) {
|
||||
if (!(this.CWMF || forTest)) {
|
||||
this.AROF = 0; // don't restore A or B for word mode --
|
||||
this.BROF = 0; // they will pop up as necessary
|
||||
} else {
|
||||
this.M = bw % 0x8000;
|
||||
this.N = (bw % 0x80000 - bw % 0x8000)/0x8000;
|
||||
|
||||
@@ -1454,9 +1459,6 @@ B5500Processor.prototype.initiate = function initiate(forTest) {
|
||||
temp * 0x8000 +
|
||||
(this.X - this.X % 0x40000000);
|
||||
}
|
||||
} else { // don't restore A or B for word mode -- will pop up as necessary
|
||||
this.AROF = 0;
|
||||
this.BROF = 0;
|
||||
}
|
||||
|
||||
this.T = this.cc.fieldIsolate(this.P, this.L*12, 12);
|
||||
@@ -1481,12 +1483,12 @@ B5500Processor.prototype.initiateAsP2 = function initiateAsP2() {
|
||||
/* Called from Central Control to initiate the processor as P2. Fetches the
|
||||
INCW from @10 and calls initiate() */
|
||||
|
||||
this.M = 0x08; // address of the INCW
|
||||
this.loadAviaM(); // A = [M]
|
||||
this.AROF = 1;
|
||||
this.T = 0x849; // inject 4111=IP1 into P2's T register
|
||||
this.M = 0x08; // address of the INCW
|
||||
this.loadBviaM(); // B = [M]
|
||||
this.AROF = 0; // make sure A is invalid
|
||||
this.T = 0x849; // inject 4111=IP1 into P2's T register
|
||||
this.TROF = 1;
|
||||
this.NCSF = 0; // make sure P2 is in control state
|
||||
this.NCSF = 0; // make sure P2 is in control state to execute the IP1
|
||||
|
||||
// Now start scheduling P2 on the Javascript thread
|
||||
this.start();
|
||||
@@ -3042,7 +3044,7 @@ B5500Processor.prototype.run = function run() {
|
||||
|
||||
case 0x0A: // XX12: TBN=Transfer blank for numeric
|
||||
this.MSFF = 1; // initialize true-false FF
|
||||
this.streamToDest(variant, function(bb, count) {
|
||||
this.streamToDest(variant, function TBN(bb, count) {
|
||||
var c = this.Z = this.cc.fieldIsolate(this.B, bb, 6);
|
||||
var result = 0;
|
||||
|
||||
@@ -3527,7 +3529,7 @@ B5500Processor.prototype.run = function run() {
|
||||
|
||||
case 0x3D: // XX75: TRN=Transfer source numerics
|
||||
this.MSFF = 0; // initialize true-false FF
|
||||
this.streamSourceToDest(variant, function(bb, c, count) {
|
||||
this.streamSourceToDest(variant, function TRN(bb, c, count) {
|
||||
|
||||
if (count == 1 && (c & 0x30) == 0x20) {
|
||||
this.MSFF = 1; // neg. sign
|
||||
@@ -3537,13 +3539,13 @@ B5500Processor.prototype.run = function run() {
|
||||
break;
|
||||
|
||||
case 0x3E: // XX76: TRZ=Transfer source zones
|
||||
this.streamSourceToDest(variant, function(bb, c, count) {
|
||||
this.streamSourceToDest(variant, function TRZ(bb, c, count) {
|
||||
this.B = this.cc.fieldInsert(this.B, bb, 2, c >>> 4);
|
||||
});
|
||||
break;
|
||||
|
||||
case 0x3F: // XX77: TRS=Transfer source characters
|
||||
this.streamSourceToDest(variant, function(bb, c, count) {
|
||||
this.streamSourceToDest(variant, function TRS(bb, c, count) {
|
||||
this.B = this.cc.fieldInsert(this.B, bb, 6, c);
|
||||
});
|
||||
break;
|
||||
@@ -4580,53 +4582,63 @@ B5500Processor.prototype.run = function run() {
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500Processor.prototype.schedule = function schedule() {
|
||||
/* Schedules the processor run time and attempts to throttle performance
|
||||
to approximate that of a real B5500 (well, at least we hope this will run
|
||||
fast enough that the performance will need to be throttled). It establishes
|
||||
B5500Processor.prototype.schedule = function schedule(which) {
|
||||
/* Schedules the processor running time and attempts to throttle performance
|
||||
to approximate that of a real B5500 -- well, at least we hope this will run
|
||||
fast enough that the performance will need to be throttled. It establishes
|
||||
a timeslice in terms of a number of processor "cycles" of 1 microsecond
|
||||
each and calls run() to execute at most that number of cycles. run()
|
||||
counts up cycles until it reaches this limit or some terminating event
|
||||
(such as a halt), then exits back here. If the processor remains active,
|
||||
this routine will reschedule itself after an appropriate delay, thereby
|
||||
throttling the performance and allowing other modules a chance at the
|
||||
Javascript execution thread */
|
||||
var clockOff; // ending time for this run() call, ms
|
||||
var delayTime; // delay until next run() for this processor, ms
|
||||
single Javascript execution thread */
|
||||
var clockOff = new Date().getTime();// ending time for the delay and the run() call, ms
|
||||
var delayTime; // delay from/until next run() for this processor, ms
|
||||
var runTime; // real-world processor running time, ms
|
||||
|
||||
this.scheduler = null;
|
||||
this.cycleLimit = B5500Processor.timeSlice;
|
||||
this.runCycles = 0;
|
||||
|
||||
this.run(); // execute syllables for the timeslice
|
||||
|
||||
clockOff = new Date().getTime();
|
||||
runTime = this.procTime;
|
||||
while (runTime < 0) {
|
||||
runTime += clockOff;
|
||||
if (which) {
|
||||
this.schedImmediate = null;
|
||||
} else {
|
||||
this.schedTimeout = null;
|
||||
}
|
||||
this.totalCycles += this.runCycles;
|
||||
delayTime = clockOff - this.delayLastStamp;
|
||||
this.procSlack += delayTime;
|
||||
if (this.delayRequested) {
|
||||
delayTime = clockOff - this.delayLastStamp - this.delayRequested;
|
||||
this.delayDeltaAvg = (this.delayDeltaAvg*(B5500Processor.delaySamples-1) +
|
||||
delayTime)/B5500Processor.delaySamples;
|
||||
delayTime - this.delayRequested)/B5500Processor.delaySamples;
|
||||
}
|
||||
|
||||
if (this.busy) {
|
||||
// delayTime is the number of milliseconds the processor is running ahead of
|
||||
// real-world time. Web browsers have a certain minimum delay. If the delay
|
||||
// is less than that, we yield to the event loop, but otherwise continue (real
|
||||
// time should eventually catch up -- we hope)
|
||||
delayTime = this.totalCycles/1000 - runTime;
|
||||
this.delayLastStamp = clockOff;
|
||||
if (delayTime < B5500CentralControl.minDelay) {
|
||||
this.cycleLimit = B5500Processor.timeSlice;
|
||||
this.runCycles = 0;
|
||||
|
||||
this.run(); // execute syllables for the timeslice
|
||||
|
||||
this.delayLastStamp = clockOff = new Date().getTime();
|
||||
this.totalCycles += this.runCycles;
|
||||
if (!this.busy) {
|
||||
this.delayRequested = 0;
|
||||
setImmediate(this.boundSchedule); // just execute pending events
|
||||
} else {
|
||||
this.scheduler = setTimeout(this.boundSchedule, delayTime);
|
||||
this.delayRequested = delayTime;
|
||||
this.procSlack += delayTime;
|
||||
runTime = this.procTime;
|
||||
while (runTime < 0) {
|
||||
runTime += clockOff;
|
||||
}
|
||||
|
||||
delayTime = this.totalCycles/1000 - runTime;
|
||||
// delayTime is the number of milliseconds the processor is running ahead of
|
||||
// real-world time. Web browsers have a certain minimum delay. If the delay
|
||||
// is less than our estimate of that minimum, we yield to the event loop but
|
||||
// otherwise continue (real time should eventually catch up -- we hope). If the
|
||||
// delay is greater than the minimum, we reschedule ourselves after that delay.
|
||||
|
||||
if (delayTime < B5500CentralControl.minDelay) {
|
||||
this.delayRequested = 0;
|
||||
this.schedImmediate = setImmediate(this.boundSchedule, 1); // just yield to the event loop
|
||||
} else {
|
||||
this.delayRequested = delayTime;
|
||||
this.schedTimeout = setTimeout(this.boundSchedule, delayTime, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -40,7 +40,7 @@ var B5500SystemConfiguration = {
|
||||
units: {
|
||||
SPO: true, // SPO keyboard/printer
|
||||
DKA: true, // Disk File Control A
|
||||
DKB: false, // Disk File Control B
|
||||
DKB: true, // Disk File Control B
|
||||
CRA: true, // Card Reader A
|
||||
CRB: false, // Card Reader B
|
||||
CPA: true, // Card Punch A
|
||||
|
||||
612
tools/B5500DiskDirFixer.html
Normal file
612
tools/B5500DiskDirFixer.html
Normal file
@@ -0,0 +1,612 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>B5500 Disk Directory Fixer Utility</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 B5500DiskDirFixer.html
|
||||
************************************************************************
|
||||
* Copyright (c) 2013, Paul Kimpel.
|
||||
* Licensed under the MIT License,
|
||||
* see http://www.opensource.org/licenses/mit-license.php
|
||||
************************************************************************
|
||||
* B5500 Disk Directory 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 = 1; // current IDB database version
|
||||
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, nameAddr, nameBlock, nameIndex) {
|
||||
/* Callback (successor) function to fix up a disk header */
|
||||
/* This version corrects converts a chosen file name to DIRCTRY/DISK */
|
||||
var areasize = directoryEnd-directoryTop-4;
|
||||
var buffer = new Uint8Array(240);
|
||||
var eu;
|
||||
var euName = euPrefix + "0";
|
||||
var h = readDiskHeader(headerBlock);
|
||||
var txn;
|
||||
|
||||
txn = disk.transaction([euName], "readwrite");
|
||||
txn.oncomplete = function(ev) {
|
||||
alert(mfid + "/" + fid + " fix-up completed successfully");
|
||||
};
|
||||
|
||||
eu = txn.objectStore(euName);
|
||||
wordsToANSI(nameBlock, 0, 30, buffer, 0);
|
||||
stringToANSI("0DIRCTRY0DISK ", buffer, nameIndex*16);
|
||||
eu.put(buffer, nameAddr);
|
||||
|
||||
headerBlock[0] = ((30*0x8000 + 30)*0x1000 + 1)*0x40 + 1;
|
||||
// 30 words/rec, 30 words/block, 1 rec/block, 1 seg/block
|
||||
headerBlock[3] = (999*0x40000 + 80001) * 0x40000 + 80001; // save=999, create+update=1980-01-01
|
||||
headerBlock[7] = areasize-1;
|
||||
headerBlock[8] = areasize;
|
||||
headerBlock[9] = 1;
|
||||
headerBlock[10] = directoryTop + 4; // address of the directory
|
||||
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 nameAddr; // directory name segment address
|
||||
var nameBlock; // directory name segment
|
||||
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;
|
||||
nameAddr = addr+15;
|
||||
nameBlock = block.slice(450, 480);
|
||||
headerAddr = addr + namex;
|
||||
bx = namex*30;
|
||||
headerBlock = block.slice(bx, bx+30);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
successor(mfid, fid, headerAddr, headerBlock, nameAddr, nameBlock, namex);
|
||||
} 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("TAPE", "COMPARE", 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/retro-B5500-Logo.png" alt="retro-B5500 Logo" style="float:left">
|
||||
Disk Directory Fixer Utility
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre id=TextPanel>
|
||||
</pre>
|
||||
|
||||
<div id=PageBottom>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -29,7 +29,7 @@ if (!window.indexedDB) { // for Safari, mostly
|
||||
window.indexedDB = window.webkitIndexedDB || window.mozIndexedDB;
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
window.addEventListener("load", function() {
|
||||
var configName = "CONFIG"; // database configuration store name
|
||||
var dbName = "B5500DiskUnit"; // IDB database name
|
||||
var dbVersion = 1; // current IDB database version
|
||||
@@ -119,7 +119,7 @@ window.onload = function() {
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function bit(word, bit) {
|
||||
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
|
||||
@@ -129,7 +129,7 @@ window.onload = function() {
|
||||
} else {
|
||||
return word % 2;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function fieldIsolate(word, start, width) {
|
||||
@@ -138,7 +138,7 @@ window.onload = function() {
|
||||
var p; // bottom portion of word power of 2
|
||||
|
||||
return (le == 0 ? word : (word - word % (p = pow2[le]))/p) % pow2[width];
|
||||
};
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function spout(text) {
|
||||
@@ -548,7 +548,7 @@ window.onload = function() {
|
||||
directoryList(function() {});
|
||||
});
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -30,7 +30,7 @@ if (!window.indexedDB) { // for Safari, mostly
|
||||
window.indexedDB = window.webkitIndexedDB || window.mozIndexedDB;
|
||||
}
|
||||
|
||||
window.onload = function() {
|
||||
window.addEventListener("load", function() {
|
||||
var configName = "CONFIG"; // database configuration store name
|
||||
var dbName = "B5500DiskUnit"; // IDB database name
|
||||
var dbVersion = 1; // current IDB database version
|
||||
@@ -132,7 +132,7 @@ window.onload = function() {
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function bit(word, bit) {
|
||||
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
|
||||
@@ -142,7 +142,7 @@ window.onload = function() {
|
||||
} else {
|
||||
return word % 2;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function fieldIsolate(word, start, width) {
|
||||
@@ -151,7 +151,7 @@ window.onload = function() {
|
||||
var p; // bottom portion of word power of 2
|
||||
|
||||
return (le == 0 ? word : (word - word % (p = pow2[le]))/p) % pow2[width];
|
||||
};
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function spout(text) {
|
||||
@@ -717,7 +717,7 @@ window.onload = function() {
|
||||
directoryList(function() {});
|
||||
});
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
598
tools/B5500DiskSystemLogFixer.html
Normal file
598
tools/B5500DiskSystemLogFixer.html
Normal file
@@ -0,0 +1,598 @@
|
||||
<!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=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 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 = 1; // current IDB database version
|
||||
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/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>
|
||||
@@ -15,21 +15,27 @@ $ CARD LIST SINGLE
|
||||
|
||||
BEGIN
|
||||
DEFINE
|
||||
LOGSEGS = 20000 #;
|
||||
WORDSPERREC = 5 #,
|
||||
WORDSPERSEG = 30 #,
|
||||
LOGRECS = 30000 #;
|
||||
SAVE FILE OUT
|
||||
LOG DISK SERIAL [1:LOGSEGS] "SYSTEM" "LOG" (1, 5, 30, SAVE 365);
|
||||
LOG DISK RANDOM [1:LOGRECS]
|
||||
"SYSTEM" "LOG" (1, WORDSPERREC, WORDSPERSEG, SAVE 365);
|
||||
ARRAY
|
||||
BUF [0:29];
|
||||
|
||||
BUF [0:WORDSPERSEG-1];
|
||||
|
||||
BUF[0] ~ 0;
|
||||
BUF[1] ~ LOGSEGS;
|
||||
BUF[1] ~ LOGRECS-1;
|
||||
BUF[2] ~ 0;
|
||||
BUF[3] ~ 0;
|
||||
BUF[4] ~ "DISKLOG";
|
||||
WRITE(LOG, 5, BUF[*]);
|
||||
WRITE(LOG[0], WORDSPERREC, BUF[*]);
|
||||
|
||||
BUF[0] ~ 4;
|
||||
BUF[1] ~ BUF[4] ~ 0;
|
||||
WRITE(LOG, 5, BUF[*]);
|
||||
WRITE(LOG[1], WORDSPERREC, BUF[*]);
|
||||
|
||||
WRITE(LOG[LOGRECS-1], WORDSPERREC, BUF[*]);
|
||||
LOCK(LOG);
|
||||
END.
|
||||
?END
|
||||
|
||||
@@ -43,7 +43,7 @@ function B5500CardPunch(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
this.endOfStacker2 = null;
|
||||
this.window = window.open("/B5500/webUI/B5500CardPunch.html", mnemonic,
|
||||
"scrollbars=no,resizable,width=700,height=500");
|
||||
this.window.addEventListener("load", function() {
|
||||
this.window.addEventListener("load", function windowLoad() {
|
||||
that.punchOnload();
|
||||
}, false);
|
||||
}
|
||||
@@ -226,15 +226,15 @@ B5500CardPunch.prototype.punchOnload = function punchOnload() {
|
||||
this.armRunout(false);
|
||||
this.setPunchReady(true);
|
||||
|
||||
this.$$("CPStartBtn").addEventListener("click", function(ev) {
|
||||
this.$$("CPStartBtn").addEventListener("click", function startClick(ev) {
|
||||
that.CPStartBtn_onclick(ev);
|
||||
}, false);
|
||||
|
||||
this.$$("CPStopBtn").addEventListener("click", function(ev) {
|
||||
this.$$("CPStopBtn").addEventListener("click", function stopClick(ev) {
|
||||
that.CPStopBtn_onclick(ev);
|
||||
}, false);
|
||||
|
||||
this.$$("CPRunoutBtn").addEventListener("click", function(ev) {
|
||||
this.$$("CPRunoutBtn").addEventListener("click", function runoutClick(ev) {
|
||||
that.CPRunoutBtn_onclick(ev);
|
||||
}, false);
|
||||
|
||||
@@ -295,7 +295,7 @@ B5500CardPunch.prototype.write = function write(finish, buffer, length, mode, co
|
||||
}
|
||||
}
|
||||
|
||||
this.timer = setTimeout(function() {
|
||||
this.timer = setTimeout(function writeDelay() {
|
||||
that.busy = false;
|
||||
finish(that.errorMask, length);
|
||||
}, 60000/this.cardsPerMinute + this.initiateStamp - new Date().getTime());
|
||||
|
||||
@@ -39,7 +39,7 @@ function B5500CardReader(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
this.doc = null;
|
||||
this.window = window.open("/B5500/webUI/B5500CardReader.html", mnemonic,
|
||||
"scrollbars=no,resizable,width=700,height=150");
|
||||
this.window.addEventListener("load", function() {
|
||||
this.window.addEventListener("load", function windowLoad() {
|
||||
that.readerOnload();
|
||||
}, false);
|
||||
|
||||
@@ -147,6 +147,7 @@ B5500CardReader.prototype.CRStartBtn_onclick = function CRStartBtn_onclick(ev) {
|
||||
var that = this;
|
||||
|
||||
if (!this.ready) {
|
||||
this.$$("CRFileSelector").value = null; // reset the control so the same file can be reloaded
|
||||
if (this.bufIndex < this.bufLength) {
|
||||
this.setReaderReady(true);
|
||||
}
|
||||
@@ -157,6 +158,7 @@ B5500CardReader.prototype.CRStartBtn_onclick = function CRStartBtn_onclick(ev) {
|
||||
B5500CardReader.prototype.CRStopBtn_onclick = function CRStopBtn_onclick(ev) {
|
||||
/* Handle the click event for the STOP button */
|
||||
|
||||
this.$$("CRFileSelector").value = null; // reset the control so the same file can be reloaded
|
||||
if (this.ready) {
|
||||
this.setReaderReady(false);
|
||||
} else if (this.eofArmed) {
|
||||
@@ -340,23 +342,23 @@ B5500CardReader.prototype.readerOnload = function readerOnload() {
|
||||
this.armEOF(false);
|
||||
this.setReaderReady(false);
|
||||
|
||||
this.$$("CRFileSelector").addEventListener("change", function(ev) {
|
||||
this.$$("CRFileSelector").addEventListener("change", function fileSelectorChange(ev) {
|
||||
that.fileSelector_onChange(ev);
|
||||
}, false);
|
||||
|
||||
this.$$("CRStartBtn").addEventListener("click", function(ev) {
|
||||
this.$$("CRStartBtn").addEventListener("click", function startClick(ev) {
|
||||
that.CRStartBtn_onclick(ev);
|
||||
}, false);
|
||||
|
||||
this.$$("CRStopBtn").addEventListener("click", function(ev) {
|
||||
this.$$("CRStopBtn").addEventListener("click", function stopClick(ev) {
|
||||
that.CRStopBtn_onclick(ev);
|
||||
}, false);
|
||||
|
||||
this.$$("CREOFBtn").addEventListener("click", function(ev) {
|
||||
this.$$("CREOFBtn").addEventListener("click", function eofClick(ev) {
|
||||
that.CREOFBtn_onclick(ev);
|
||||
}, false);
|
||||
|
||||
this.$$("CRProgressBar").addEventListener("click", function(ev) {
|
||||
this.$$("CRProgressBar").addEventListener("click", function progressClick(ev) {
|
||||
that.CRProgressBar_onclick(ev);
|
||||
}, false);
|
||||
};
|
||||
@@ -396,7 +398,7 @@ B5500CardReader.prototype.read = function read(finish, buffer, length, mode, con
|
||||
this.setReaderReady(false);
|
||||
}
|
||||
|
||||
this.timer = setTimeout(function() {
|
||||
this.timer = setTimeout(function readDelay() {
|
||||
that.busy = false;
|
||||
finish(that.errorMask, length);
|
||||
}, 60000/this.cardsPerMinute + this.initiateStamp - new Date().getTime());
|
||||
|
||||
@@ -1319,16 +1319,16 @@ window.addEventListener("load", function() {
|
||||
/* Performs a B5500 Cold Start by initializing the directory structure on
|
||||
the disk, building segment 0, and loading the bootstrap, overwriting (and
|
||||
destroying) whatever else was there before */
|
||||
var buffer = new Uint8Array(240);
|
||||
var eu;
|
||||
var buffer = new Uint8Array(240); // one-segment buffer area
|
||||
var eu; // IDB object store for EU0
|
||||
var euName = euPrefix + "0";
|
||||
var fileLabels = new Uint8Array(240);
|
||||
var fileLabels = new Uint8Array(240); // one-segment buffer for file names
|
||||
var fileNr = 0;
|
||||
var info = [];
|
||||
var segNr = directoryEnd + 4;
|
||||
var shar = [];
|
||||
var txn;
|
||||
var zeroes = new Uint8Array(240);
|
||||
var info = []; // configuration options segment
|
||||
var segNr = null; // next address to allocate (set by initializeDirectory)
|
||||
var shar = []; // segment 0 buffer
|
||||
var txn; // IDB transaction object
|
||||
var zeroes = new Uint8Array(240); // buffer of binary zeroes
|
||||
var x;
|
||||
|
||||
function loadBootstrap() {
|
||||
@@ -1664,12 +1664,13 @@ window.addEventListener("load", function() {
|
||||
stringToANSI("0000001?", fileLabels, labelx-16); // @114, last-entry marker
|
||||
stringToANSI("00000000", fileLabels, 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;
|
||||
header[0] = ((30*0x8000 + 30)*0x1000 + 1)*0x40 + 1;
|
||||
// 30 words/rec, 30 words/block, 1 rec/block, 1 seg/block
|
||||
header[3] = (999*0x40000 + 80001) * 0x40000 + 80001; // save=999, create+update=1980-01-01
|
||||
header[7] = areas*areasize-1; // record count
|
||||
header[8] = areasize; // row size
|
||||
header[9] = areas; // number of areas
|
||||
header[10] = segNr; // row[0] address
|
||||
wordsToANSI(header, 0, 30, buffer, 0);
|
||||
eu.put(buffer, directoryTop + 18 - fileNr);
|
||||
|
||||
@@ -1689,15 +1690,17 @@ window.addEventListener("load", function() {
|
||||
stringToANSI("0000001?", fileLabels, labelx-16); // @114, last-entry marker
|
||||
stringToANSI("00000000", fileLabels, labelx-8);
|
||||
}
|
||||
header[0] = ((5*0x8000 + 30)*0x8000 + 6)*0x1000 + 1;
|
||||
header[0] = ((5*0x8000 + 30)*0x1000 + 6)*0x40 + 1;
|
||||
// 5 words/rec, 30 words/block, 6 rec/block, 1 seg/block
|
||||
header[3] = (365*0x40000 + 80001) * 0x40000 + 80001; // save=365, create+update=1980-01-01
|
||||
header[7] = -1; // currently has no records
|
||||
header[8] = areasize;
|
||||
header[3] = (999*0x40000 + 80001) * 0x40000 + 80001; // save=999, create+update=1980-01-01
|
||||
header[7] = areasize*6-1; // record count
|
||||
header[8] = areasize; // row size
|
||||
header[9] = 1; // number of areas
|
||||
header[10] = 0; // row address unallocated
|
||||
header[10] = segNr; // row[0] address
|
||||
wordsToANSI(header, 0, 30, buffer, 0);
|
||||
eu.put(buffer, directoryTop + 18 - fileNr);
|
||||
|
||||
segNr += areasize;
|
||||
fileNr++;
|
||||
}
|
||||
|
||||
@@ -1745,9 +1748,9 @@ window.addEventListener("load", function() {
|
||||
pow2[47-43] + // 43: type file open
|
||||
pow2[47-42] + // 42: call TERMINATE procedure
|
||||
// 41: initialize date @ H/L
|
||||
// 40: initialize time @ H/L
|
||||
pow2[47-40] + // 40: initialize time @ H/L
|
||||
// 39: use only one breakout tape
|
||||
pow2[47-36] + // 38: automatically print pbt
|
||||
pow2[47-38] + // 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
|
||||
@@ -1818,8 +1821,13 @@ window.addEventListener("load", function() {
|
||||
wordsToANSI(info, 0, 30, buffer, 0);
|
||||
eu.put(buffer, directoryTop);
|
||||
|
||||
// Create a pseudo file for the disk directory itself
|
||||
segNr = directoryTop + 4; // address of the directory
|
||||
enterFile("DIRCTRY", "DISK", 1, directoryEnd-directoryTop-4);
|
||||
segNr = directoryEnd + 4; // next address to allocate
|
||||
|
||||
// Create a file entry for the system log
|
||||
createSystemLog(20000);
|
||||
createSystemLog(10000);
|
||||
/** enterFile("SYSTEM", "LOG", 1, 20000); **/
|
||||
|
||||
// Store the directory labels segment
|
||||
|
||||
@@ -42,7 +42,7 @@ function B5500DummyPrinter(mnemonic, unitIndex, designate, statusChange, signal)
|
||||
this.endOfPaper = null;
|
||||
this.window = window.open("/B5500/webUI/B5500DummyPrinter.html", mnemonic,
|
||||
"scrollbars,resizable,width=600,height=500");
|
||||
this.window.addEventListener("load", function() {
|
||||
this.window.addEventListener("load", function windowOnLoad() {
|
||||
that.printerOnload();
|
||||
}, false);
|
||||
}
|
||||
@@ -116,7 +116,7 @@ B5500DummyPrinter.prototype.printerOnload = function printerOnload() {
|
||||
this.window.moveTo(40, 40);
|
||||
this.window.resizeTo(1000, screen.availHeight*0.80);
|
||||
|
||||
this.window.addEventListener("click", function(ev) {
|
||||
this.window.addEventListener("click", function windowOnClick(ev) {
|
||||
if (ev.detail == 2) { // check for left-button double-click
|
||||
that.ripPaper(ev);
|
||||
}
|
||||
@@ -154,11 +154,13 @@ B5500DummyPrinter.prototype.write = function write(finish, buffer, length, mode,
|
||||
this.errorMask = 0;
|
||||
text = String.fromCharCode.apply(null, buffer.subarray(0, length));
|
||||
//console.log("WRITE: L=" + length + ", M=" + mode + ", C=" + control + " : " + text);
|
||||
this.appendLine(text + "\n");
|
||||
if (control > 1) {
|
||||
this.appendLine("\n");
|
||||
} else if (control < 0) {
|
||||
this.paper.appendChild(this.doc.createElement("hr"));
|
||||
if (length || control) {
|
||||
this.appendLine(text + "\n");
|
||||
if (control > 1) {
|
||||
this.appendLine("\n");
|
||||
} else if (control < 0) {
|
||||
this.paper.appendChild(this.doc.createElement("hr"));
|
||||
}
|
||||
}
|
||||
|
||||
this.timer = setTimeout(this.signal,
|
||||
|
||||
@@ -30,8 +30,9 @@ function B5500SPOUnit(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
this.statusChange = statusChange; // external function to call for ready-status change
|
||||
this.signal = signal; // external function to call for special signals (e.g,. SPO input request)
|
||||
|
||||
this.timer = null; // setTimeout() token
|
||||
this.initiateStamp = 0; // timestamp of last initiation (set by IOUnit)
|
||||
this.inTimer = null; // input setTimeout() token
|
||||
this.outTimer = null; // output setTimeout() token
|
||||
|
||||
this.clear();
|
||||
|
||||
@@ -49,7 +50,7 @@ function B5500SPOUnit(mnemonic, unitIndex, designate, statusChange, signal) {
|
||||
this.endOfPaper = null;
|
||||
this.window = window.open("/B5500/webUI/B5500SPOUnit.html", mnemonic,
|
||||
"scrollbars,resizable,width=600,height=500");
|
||||
this.window.addEventListener("load", function() {
|
||||
this.window.addEventListener("load", function windowOnLoad() {
|
||||
that.spoOnload();
|
||||
}, false);
|
||||
}
|
||||
@@ -224,19 +225,19 @@ B5500SPOUnit.prototype.outputChar = function outputChar() {
|
||||
that.printChar(that.buffer[that.bufIndex]);
|
||||
that.bufIndex++;
|
||||
that.printCol++;
|
||||
this.timer = setTimeout(that.outputChar, delay);
|
||||
this.outTimer = setTimeout(that.outputChar, delay);
|
||||
} else { // set up for the final CR/LF
|
||||
that.printCol = 72;
|
||||
this.timer = setTimeout(that.outputChar, delay);
|
||||
this.outTimer = setTimeout(that.outputChar, delay);
|
||||
}
|
||||
} else if (that.printCol == 72) { // delay to fake the output of a carriage-return
|
||||
that.printCol++;
|
||||
this.timer = setTimeout(that.outputChar, delay+that.charPeriod);
|
||||
this.outTimer = setTimeout(that.outputChar, delay+that.charPeriod);
|
||||
} else { // actually output the CR/LF
|
||||
that.appendEmptyLine();
|
||||
if (that.bufIndex < that.bufLength) {
|
||||
that.printCol = 0; // more characters to print after the CR/LF
|
||||
this.timer = setTimeout(that.outputChar, delay);
|
||||
this.outTimer = setTimeout(that.outputChar, delay);
|
||||
} else { // message text is exhausted
|
||||
that.finish(that.errorMask, that.bufLength); // report finish with any errors
|
||||
if (that.spoLocalRequested) {
|
||||
@@ -299,21 +300,27 @@ B5500SPOUnit.prototype.keyPress = function keyPress(ev) {
|
||||
if (this.printCol < 72) {
|
||||
this.printCol++;
|
||||
}
|
||||
this.timer = setTimeout(function() {that.printChar(c)}, nextTime-stamp);
|
||||
this.inTimer = setTimeout(function keyPressChar() {
|
||||
that.printChar(c);
|
||||
}, nextTime-stamp);
|
||||
}
|
||||
if (c == 126) { // "~" (B5500 group-mark)
|
||||
c = this.keyFilter[c];
|
||||
if (this.printCol < 72) {
|
||||
this.printCol++;
|
||||
}
|
||||
this.timer = setTimeout(function() {that.printChar(c)}, nextTime-stamp);
|
||||
this.inTimer = setTimeout(function keyPressGM() {
|
||||
that.printChar(c);
|
||||
}, nextTime-stamp);
|
||||
this.nextCharTime = nextTime + this.charPeriod;
|
||||
this.terminateInput();
|
||||
}
|
||||
} else if (this.spoState == this.spoLocal) {
|
||||
if (c >= 32 && c <= 126) {
|
||||
c = this.keyFilter[c];
|
||||
this.timer = setTimeout(function() {that.printChar(c)}, nextTime-stamp);
|
||||
this.inTimer = setTimeout(function keyPressLocalChar() {
|
||||
that.printChar(c);
|
||||
}, nextTime-stamp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +361,7 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
|
||||
switch (this.spoState) {
|
||||
case this.spoInput:
|
||||
case this.spoLocal:
|
||||
this.timer = setTimeout(this.backspaceChar, nextTime-stamp);
|
||||
this.inTimer = setTimeout(this.backspaceChar, nextTime-stamp);
|
||||
this.nextCharTime = nextTime;
|
||||
result = false;
|
||||
break;
|
||||
@@ -368,7 +375,7 @@ B5500SPOUnit.prototype.keyDown = function keyDown(ev) {
|
||||
result = false;
|
||||
break
|
||||
case this.spoLocal:
|
||||
this.timer = setTimeout(function() {
|
||||
this.inTimer = setTimeout(function keyDownLocal() {
|
||||
that.appendEmptyLine();
|
||||
}, nextTime-stamp+this.charPeriod);
|
||||
this.nextCharTime = nextTime;
|
||||
@@ -440,41 +447,41 @@ B5500SPOUnit.prototype.spoOnload = function spoOnload() {
|
||||
|
||||
this.window.addEventListener("beforeunload", this.beforeUnload, false);
|
||||
|
||||
this.window.addEventListener("keypress", function(ev) {
|
||||
this.window.addEventListener("keypress", function windowKeyPress(ev) {
|
||||
that.keyPress(ev);
|
||||
}, false);
|
||||
|
||||
this.window.addEventListener("keydown", function(ev) {
|
||||
this.window.addEventListener("keydown", function windowKeyDown(ev) {
|
||||
that.keyDown(ev);
|
||||
}, false);
|
||||
|
||||
this.$$("SPORemoteBtn").addEventListener("click", function() {
|
||||
this.$$("SPORemoteBtn").addEventListener("click", function remoteClick() {
|
||||
that.setRemote();
|
||||
}, false);
|
||||
|
||||
this.$$("SPOLocalBtn").addEventListener("click", function() {
|
||||
this.$$("SPOLocalBtn").addEventListener("click", function localClick() {
|
||||
that.setLocal();
|
||||
}, false);
|
||||
|
||||
this.$$("SPOInputRequestBtn").addEventListener("click", function() {
|
||||
this.$$("SPOInputRequestBtn").addEventListener("click", function inputRequestClick() {
|
||||
if (that.spoState == that.spoRemote || that.spoState == that.spoOutput) {
|
||||
that.addClass(that.$$("SPOInputRequestBtn"), "yellowLit");
|
||||
that.signal();
|
||||
}
|
||||
}, false);
|
||||
|
||||
this.$$("SPOErrorBtn").addEventListener("click", function() {
|
||||
this.$$("SPOErrorBtn").addEventListener("click", function errorClick() {
|
||||
that.cancelInput();
|
||||
}, false);
|
||||
|
||||
this.$$("SPOEndOfMessageBtn").addEventListener("click", function() {
|
||||
this.$$("SPOEndOfMessageBtn").addEventListener("click", function endOfMessageClick() {
|
||||
that.terminateInput();
|
||||
}, false);
|
||||
|
||||
for (x=0; x<32; x++) {
|
||||
this.appendEmptyLine();
|
||||
}
|
||||
this.printText("retro-B5500 Emulator Version " + B5500CentralControl.version, function() {
|
||||
this.printText("retro-B5500 Emulator Version " + B5500CentralControl.version, function initComplete() {
|
||||
this.window.focus();
|
||||
that.setRemote();
|
||||
that.appendEmptyLine();
|
||||
@@ -582,8 +589,11 @@ B5500SPOUnit.prototype.writeInterrogate = function writeInterrogate(finish, cont
|
||||
B5500SPOUnit.prototype.shutDown = function shutDown() {
|
||||
/* Shuts down the device */
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
if (this.inTimer) {
|
||||
clearTimeout(this.inTimer);
|
||||
}
|
||||
if (this.outTimer) {
|
||||
clearTimeout(this.outTimer);
|
||||
}
|
||||
this.window.removeEventListener("beforeunload", this.beforeUnload, false);
|
||||
this.window.close();
|
||||
|
||||
@@ -643,7 +643,7 @@ function displayCentralControl() {
|
||||
|
||||
cells = $$("CCPeripheralRow").cells;
|
||||
for (x=17; x<=47; x++) {
|
||||
cells[x-16].className = (cc.bit(usm, x) ? (cc.bit(ubm, x) ? "busy" : "ready") : "notReady");
|
||||
cells[x-16].className = (cc.bitTest(usm, x) ? (cc.bitTest(ubm, x) ? "busy" : "ready") : "notReady");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user