mirror of
https://github.com/pkimpel/retro-b5500.git
synced 2026-04-14 17:05:41 +00:00
1. Finalize and release emulator version 0.05.
2. Fix (we hope) the nasty problem that was causing Invalid Address out of COM5 after large compiles -- bug in MSCW/MSFF tracing in Processor.exitSubroutine(). 3. Implement ability to do a hardware load without initiating P1 in CentralControl (used by SyllableDebugger). 4. De-anonymize object prototype method functions in Processor as an aid when using JS debuggers. 5. Rework storeForInterrupt() and initiate() in Processor. 6. Replace bitmask "AND"s with power-of-two "MOD"s in many places in Processor. 7. Replace many bit(), fieldIsolate(), fieldInsert() calls with mod/div expressions in Processor. 8. Fix a problem existing normalization when an Integer Overflow occurs in Integer Store syllables. 9. Fix missing and superfluous break statements in Processor.run() switch statements. 10. Fix incorrect C-register restoration in Character Mode RCA syllable. 11. Correct test for continuity bit in Processor PRL syllable coding. 12. Correct missing "this." in Processor BRT syllable coding. 13. Change way that delayTime and Processor.procSlack is computed in Processor.run(). 14. Correct BCL translation bug and directory EOF detection in B5500ColdLoader. 15. Attempt to initialize SYSTEM/LOG in B5500ColdLoader (not working yet). 16. Implement new retro-B5500-Logo.png. 17. Implement Hardware Load button in SyllableDebugger. 18. Release tools/B5500DiskDirList.html utility. 19. Release tools/B5500DiskFileList.html utility with PBD file special formattring. 20. Restrict directory complement to EU0 in B5500ColdLoader.html. 21. Miscellaneous improvements to comments and cosmetics.
This commit is contained in:
@@ -22886,3 +22886,169 @@ BEGIN REAL RCW=+0,MSCW=-2; 38001000
|
||||
FPB[FNUM ]:=MFID:=FID; %126-38013030
|
||||
FPB[FNUM+1]:=FID :=USERCODE[P1MIX]; %126-38013040
|
||||
END; %126-38013050
|
||||
IF NFLAG(FIX[14]~FLAG(FILEHEADER(MFID 38013100
|
||||
$ SET OMIT = NOT SHAREDISK 38013199
|
||||
,FID&FIB[5][1:45:1],FIB[8].[20:5] 38013300
|
||||
,FIB[8].[25:23],BLEN,RLEN,STATE)))<6 THEN 38013400
|
||||
BEGIN P(DEL); 38013500
|
||||
TOG:= 1; 38013510
|
||||
$ SET OMIT = NOT SHAREDISK 38013519
|
||||
GO TO EXIT; 38013600
|
||||
END; 38013700
|
||||
IF FIB[8].[20:28]!0 THEN FPB[FNUM+2].[18:30]~DATE ELSE 38013900
|
||||
BEGIN% OLD FILE,VERIFY LABEL EQUATION DATE IF ANY 38014000
|
||||
HEADER := FIB[14];% 38014100
|
||||
STREAM(H:=HEADER[3].[30:18],B:=[T2]); 38014200
|
||||
BEGIN SI:=LOC H; DS:=8 DEC; END;% 38014300
|
||||
AGN: IF CDATE NEQ 0 AND CDATE NEQ HEADER[3].[30:18] THEN38014400
|
||||
BEGIN% WRITE DATE CHECK MESSAGE 38014500
|
||||
DOLITTLE(FALSE, 38014600
|
||||
VWY&VOF[36:42:6]&VOK[30:42:6], 38014610
|
||||
"#DAT CK"," =00000"&T2[18:18:30],MFID); 38014620
|
||||
IF TERMSET(P1MIX) THEN 38014700
|
||||
BEGIN 38014800
|
||||
FORGETSPACE(DIRECTORYSEARCH(MFID,FID, 38014900
|
||||
$ SET OMIT = NOT SHAREDISK 38014949
|
||||
FIB[5].[13:3]+10)); 38015000
|
||||
GO TO INITIATE; 38015100
|
||||
END;% 38015200
|
||||
IF P(NT6,DUP)=VOK OR P(XCH)=VOF THEN CDATE~0; 38015400
|
||||
GO AGN 38015500
|
||||
END;% VERIFICATION 38015600
|
||||
FPB[FNUM+2].[18:30]:=T2;% BCL DATE 38015700
|
||||
END OLD FILES;% 38015800
|
||||
STARTIMING(FNUM,18);% 38015900
|
||||
FPB:=PRT[P1MIX,3]; % STARTIMING MOVES THE FPB 38015950
|
||||
END;% 38016000
|
||||
HEADER~FIB[14];% 38020000
|
||||
KIND~4; U~18;% 38021000
|
||||
MODE~0;% 38022000
|
||||
IF NOT COBOL THEN UNLABELED~1;% 38023000
|
||||
CNTCTL~BLEN{1023;% 38024000
|
||||
$ SET OMIT = NOT SHAREDISK 38024004
|
||||
IF COBOL>0 AND (FIB[13].[22:1] OR TYPE=10 OR TYPE=26) THEN 38024100
|
||||
BEGIN COBOL:=3; %IF COBOL-IO OR COBOL-RANDOM 38024200
|
||||
BLEN := BLEN + RLEN; % THEN CHANGE BUFFSIZE TO 38024300
|
||||
END; % BUFFSIZE + RECSIZE 38024400
|
||||
GETBUFFERS((IF CNTCTL THEN BLEN% 38025000
|
||||
ELSE ((BLEN+29) DIV 30)|30)+1,% 38026000
|
||||
NBUFS,U,ALPHA);% 38027000
|
||||
IF COBOL = 3 THEN %IF COBOL-IO OR COBOL-RANDOM 38027100
|
||||
BEGIN COBOL := 1; % THEN CHANGE BUFFSIZE TO 38027200
|
||||
BLEN := BLEN - RLEN; % BUFFSIZE - RECSIZE 38027300
|
||||
END; % (SEE ABOVE 38027400
|
||||
FIB[16]~M[ALPHA]&CNTCTL[23:47:1]&10[24:47:1]% 38028000
|
||||
&((BLEN+29) DIV 30)[27:42:6]% 38029000
|
||||
&(IF CNTCTL THEN BLEN ELSE 1023)[8:38:10]% 38030000
|
||||
&TINU[18][3:3:5] OR M OR IOMASK;% 38031000
|
||||
FIB[16].[2:1]:=(HEADER.[31:2] AND (IO+1))!0; 38032000
|
||||
FIB[5].[1:1]:= NOT FIB[16].[2:1]; 38033000
|
||||
IF FIB[5].[1:1] THEN 38034000
|
||||
FOR MASK:=10 STEP 1 UNTIL 29 DO HEADER[MASK]:=0; 38035000
|
||||
FIB[19]~(IF DIREC THEN BLEN-RLEN+1 ELSE 1) 38036000
|
||||
INX FIB[16]&0[27:27:6]; 38037000
|
||||
IF STATE.[46:2]!0 THEN FIB[19].[8:10]~RLEN;% 38038000
|
||||
FS[P1MIX,(T2:=(FNUM DIV ETRLNG)).[40:4]]~(*P(DUP)) OR 38039000
|
||||
(TWO(0&T2[43:44:4])|((NOT HEADER).[31:2])); 38040000
|
||||
T2~IF COBOL THEN 0 ELSE FIB[19].[33:15]-FIB[16].[33:15]; 38041000
|
||||
FIB[10].[3:15]:=M[ALPHA]-2; %HEAD OF BUFFER RING 38041100
|
||||
FOR MASK~0 STEP 1 UNTIL NBUFS-1 DO% 38042000
|
||||
M[ALPHA+MASK]~(P(DUP,LOD)+T2)% 38043000
|
||||
&P(FLAG(FIB[19-ABS(3|COBOL)]),XCH)[CTC]; 38044000
|
||||
FIB[16]:=FIB[16] OR M; 38045000
|
||||
FIB[5].[45:1]~0; 38045100
|
||||
IF P([FIB[14]],LOD).[FF]=2 THEN FIB[5].[11:2]~1;%INPUT ONLY.38045105
|
||||
IF HEADER[4].[10:1] AND NOT IO THEN 38045110
|
||||
FILEMESS(-"CODE ","FILE ",MFID,FID,0,0,0); 38045120
|
||||
$ SET OMIT = NOT(PACKETS) 38045149
|
||||
IF PSEUDOMIX[P1MIX]!0 THEN 38045150
|
||||
IF NOT FIB[5].[41:1] THEN 38045155
|
||||
FILEMESSAGE((IF IO THEN " IN " ELSE "OUT") 38045160
|
||||
&TINU[U][6:30:18], IF ACCESS=0 THEN " SER " 38045200
|
||||
ELSE IF ACCESS=1 THEN IF TYPE=26 THEN " PRO " 38045300
|
||||
ELSE " RDM " ELSE " UPD ", 38045310
|
||||
MFID,FID,0,0,0,64); 38045400
|
||||
$ POP OMIT 38045501
|
||||
END DISKSETUP;% 38046000
|
||||
P(RCW,MSCW,STF); 38047000
|
||||
RCW:=RCW&P(XCH)[CTC]; 38048000
|
||||
DISKSETUP; 38049000
|
||||
IF COBOL<0 THEN % ADJUST UPPER BOUND FOR COBOL 68 38049200
|
||||
BEGIN MASK ~ (IF IO AND NOT FIB[13].[22:1] 38049300
|
||||
THEN HEADER[7] 38049400
|
||||
ELSE (((HEADER[9] | HEADER[1]) DIV 38049500
|
||||
HEADER[0].[42:6]) | HEADER[0].[30:12]) - 1);38049600
|
||||
IF FIB[3]=0 OR FIB[3]>MASK THEN FIB[3]~MASK; %LESSOR OF 2 EVILS38049700
|
||||
END; 38049800
|
||||
IF P(TYPE,DUP)=10 OR P(XCH)=26 THEN 38050000
|
||||
BEGIN 38051000
|
||||
IF COBOL<1 THEN % ALGOL OR COBOL 68 38052000
|
||||
FOR MASK ~ 0 STEP 1 UNTIL NBUFS-1 DO 38053000
|
||||
IF COBOL THEN M[M[ALPHA+MASK] INX NOT 2] ~ NOT 0 38053500
|
||||
ELSE M[ALPHA+MASK]~P(DUP,LOD)&1[27:47:1]; 38054000
|
||||
FIB[6]~FIB[7]~0;% 38055000
|
||||
FIB[17]~IF IO THEN 0 ELSE BLEN;% 38056000
|
||||
END ELSE 38057000
|
||||
BEGIN 38058000
|
||||
T2~(MFID~FIB[16).[33:15];% 38059000
|
||||
FIV7~FIB[7]; 38060000
|
||||
IF COBOL THEN% 38061000
|
||||
BEGIN IF COBOL>0 THEN 38062000
|
||||
IF NOT (FIB7=0 OR FIB[13].[22:1]) THEN 38062500
|
||||
BEGIN FIB7 ~ FIB7 - 1; 38063000
|
||||
OPTIONAL ~ NBUFS - 1; 38063500
|
||||
END ELSE OPTIONAL ~ NBUFS - 2 38064000
|
||||
ELSE BEGIN % COBOL 68 38064200
|
||||
OPTIONAL ~ NBUFS - 1; 38064400
|
||||
IF DIREC THEN FIB7 ~ FIB[7] ~ FIB[3]; 38064600
|
||||
END; 38065000
|
||||
FID~FIB[16];% 38066000
|
||||
MASK~0;% 38067000
|
||||
END ELSE% 38068000
|
||||
BEGIN OPTIONAL~NBUFS-1;% 38069000
|
||||
MASK~(FID~FIB[19]).[33:15]-T2;% 38070000
|
||||
END;% 38071000
|
||||
IF (STATE.[46:2]!0 AND NOT COBOL) OR IO THEN 38072000
|
||||
IF M[ALPHA].]2:1] THEN 38073000
|
||||
FOR T1~0 STEP 1 UNTIL OPTIONAL DO% 38074000
|
||||
BEGIN IF (M[T2]:= 38074500
|
||||
DISKADDRESS(FPB[FNUM], FPB[FNUM+1], FPB[FNUM+3], 38075000
|
||||
FORMS:=((HEADER[0].[30:12]|T1)&DIREC[1:47:1])+FIB7,38075500
|
||||
HEADER, IO&(NOT HEADER[4])[46:47:1])) > 1 THEN 38076000
|
||||
BEGIN 38076500
|
||||
IF (USERCODE[P1MIX] EQV MCP)!NOT 0 THEN 38077000
|
||||
IF P(M[MFID],DUP).[3:6]=0 AND 38077500
|
||||
P(XCH)<DIRDSK|DSKTOG THEN 38078000
|
||||
BEGIN 38078500
|
||||
TERMINATE(P1MIX); 38079000
|
||||
TERMINALMESSAGE(30); 38079500
|
||||
END; 38080000
|
||||
IOREQUEST(FLAG(FID),MFID&1[24:47:1],M[T2-2]); 38080500
|
||||
M[ALPHA]:=FLAG(MFID)&0[26:26:7] AND NOT 38081000
|
||||
(M OR IOMASK); 38081250
|
||||
END ELSE 38081500
|
||||
IF M[T2]=0 THEN % EOF IF INPUT, FULL HDR IF OUTPT38081750
|
||||
M[ALPHA]:=P(DUP,LOD)&1[27:47:1] AND NOT M; 38082000
|
||||
IF COBOL<0 THEN M[M[ALPHA] INX NOT 2] ~ 38082400
|
||||
(IF FORMS}0 THEN FORMS DIV FIB[11] ELSE NOT 0); 38082500
|
||||
STREAM(N~NBUFS-1,T~M[ALPHA],ALPHA);% 38083000
|
||||
BEGIN SI~ALPHA; SI~SI+8; DS~N WDS;% 38084000
|
||||
SI~LOC T; DS~WDS;% 38085000
|
||||
END;% 38086000
|
||||
MFID.[33:15]~T2~M[T2-2].[18:15];% 38087000
|
||||
FID.[33:15]~T2+MASK;% 38088000
|
||||
END;% 38089000
|
||||
IF (NBUFS-1)!OPTIONAL THEN FIB[16].[33:15]~M[ALPHA] ;% 38090000
|
||||
FORMS~(FORMS~FIB7 MOD HEADER[0].[30:12])|RLEN; 38091000
|
||||
SLEEP([M[ALPHA]],IOMASK);% 38092000
|
||||
IF COBOL } 0 THEN % NOT COBOL 68 38092900
|
||||
IF FIB[13].[22:1]THEN M[ALPHA].[33:15]~FIB[16]INX 1 ELSE 38093000
|
||||
M[ALPHA].[33:15]~FIB[16].[33:15]+FORMS+1;% 38094000
|
||||
IF (NBUFS-1)!OPTIONAL AND IO AND NOT FIB[13].[22:1] THEN 38095000
|
||||
FIB[ 17 ]~0 ELSE 38096000
|
||||
FIB[17]~IF DIREC THEN FORMS~RLEN% 38097000
|
||||
ELSE BLEN-FORMS;% 38098000
|
||||
END; 38099000
|
||||
EXIT: 38099100
|
||||
P(P&RCW[CTC],0,RDS,0,XCH,P&P[CTF],STF); 38100000
|
||||
END DISKFILEOPEN; 38101000
|
||||
|
||||
@@ -55,7 +55,6 @@ function B5500CentralControl() {
|
||||
|
||||
// Establish contexts for asynchronously-called methods
|
||||
this.boundTock = B5500CentralControl.bindMethod(this.tock, this);
|
||||
this.boundLoadComplete = B5500CentralControl.bindMethod(this.loadComplete, this);
|
||||
|
||||
this.clear(); // Create and initialize the Central Control state
|
||||
}
|
||||
@@ -63,7 +62,7 @@ function B5500CentralControl() {
|
||||
/**************************************/
|
||||
/* Global constants */
|
||||
|
||||
B5500CentralControl.version = "0.04";
|
||||
B5500CentralControl.version = "0.05";
|
||||
|
||||
B5500CentralControl.rtcTick = 1000/60; // Real-time clock period, milliseconds
|
||||
|
||||
@@ -156,7 +155,7 @@ B5500CentralControl.bindMethod = function(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() {f.apply(context, arguments)};
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
@@ -420,7 +419,7 @@ B5500CentralControl.prototype.signalInterrupt = function() {
|
||||
: this.CCI16F ? 0x1F // @37: Disk file 2 read check finished
|
||||
: p1.I & 0x04 ? 0x32 // @62: P1 stack overflow
|
||||
: p1.I & 0xF0 ? (p1.I >>> 4) + 0x30 // @64-75: P1 syllable-dependent
|
||||
: (p2 = this.P2) ?
|
||||
: (p2 = this.P2) ? // Yes, Virginia, this should actually be an assignment...
|
||||
( p2.I & 0x01 ? 0x20 // @40: P2 memory parity error
|
||||
: p2.I & 0x02 ? 0x21 // @41: P2 invalid address error
|
||||
: p2.I & 0x04 ? 0x22 // @42: P2 stack overflow
|
||||
@@ -690,7 +689,7 @@ B5500CentralControl.prototype.halt = function() {
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CentralControl.prototype.loadComplete = function loadComplete() {
|
||||
B5500CentralControl.prototype.loadComplete = function loadComplete(dontStart) {
|
||||
/* Monitors an initial load I/O operation for complete status.
|
||||
When complete, initiates P1 */
|
||||
var completed = false; // true if some I/O Unit finished
|
||||
@@ -711,21 +710,25 @@ B5500CentralControl.prototype.loadComplete = function loadComplete() {
|
||||
completed = true;
|
||||
this.CCI11F = 0;
|
||||
this.AD4F = 0;
|
||||
//} else { // Nothing finished yet (or there was an error)
|
||||
// this.loadTimer = setTimeout(this.boundLoadComplete, 1000);
|
||||
}
|
||||
|
||||
if (completed) {
|
||||
this.loadTimer = null;
|
||||
this.LOFF = 0;
|
||||
this.P1.preset(0x10); // start execution at C=@20
|
||||
this.P1.start(); // let'er rip
|
||||
if (!dontStart) {
|
||||
this.P1.start(); // let'er rip
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
B5500CentralControl.prototype.load = function() {
|
||||
/* Initiates a Load operation to start the system */
|
||||
B5500CentralControl.prototype.load = function(dontStart) {
|
||||
/* Initiates a Load operation to start the system. If "dontStart" is truthy, then
|
||||
only the MCP bootstrap is loaded into memory -- P1 is not started */
|
||||
var boundLoadComplete = (function(that, dontStart) {
|
||||
return function() {return that.loadComplete(dontStart)}
|
||||
})(this, dontStart);
|
||||
|
||||
if (this.P1 && !this.P1.busy) {
|
||||
this.clear();
|
||||
@@ -734,18 +737,18 @@ B5500CentralControl.prototype.load = function() {
|
||||
this.LOFF = 1;
|
||||
if (this.IO1 && this.IO1.REMF && !this.AD1F) {
|
||||
this.AD1F = 1;
|
||||
this.IO1.initiateLoad(this.cardLoadSelect, this.boundLoadComplete);
|
||||
this.IO1.initiateLoad(this.cardLoadSelect, boundLoadComplete);
|
||||
} else if (this.IO2 && this.IO2.REMF && !this.AD2F) {
|
||||
this.AD2F = 1;
|
||||
this.IO2.initiateLoad(this.cardLoadSelect, this.boundLoadComplete);
|
||||
this.IO2.initiateLoad(this.cardLoadSelect, boundLoadComplete);
|
||||
} else if (this.IO3 && this.IO3.REMF && !this.AD3F) {
|
||||
this.AD3F = 1;
|
||||
this.IO3.initiateLoad(this.cardLoadSelect, this.boundLoadComplete);
|
||||
this.IO3.initiateLoad(this.cardLoadSelect, boundLoadComplete);
|
||||
} else if (this.IO4 && this.IO4.REMF && !this.AD4F) {
|
||||
this.AD4F = 1;
|
||||
this.IO4.initiateLoad(this.cardLoadSelect, this.boundLoadComplete);
|
||||
this.IO4.initiateLoad(this.cardLoadSelect, boundLoadComplete);
|
||||
} else {
|
||||
this.CCI04F = 1; // set I/O busy interrupt
|
||||
this.CCI04F = 1; // set I/O busy interrupt
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -777,7 +780,7 @@ B5500CentralControl.prototype.loadTest = function(buf, loadAddr) {
|
||||
}
|
||||
|
||||
if (!this.poweredUp) {
|
||||
throw "cc.loadTest: Cannot load with system powered off"
|
||||
throw "cc.loadTest: Cannot load with system powered off";
|
||||
} else {
|
||||
while (bytes > 6) {
|
||||
word = data.getUint8(x)* 0x10000000000 +
|
||||
@@ -876,23 +879,23 @@ B5500CentralControl.prototype.configureSystem = function() {
|
||||
default:
|
||||
return function() {};
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ***** !! inhibit for now ***** // this.DD = new B5500DistributionAndDisplay(this);
|
||||
|
||||
// Configure the processors
|
||||
if (cfg.PA) {this.PA = new B5500Processor("A", this)};
|
||||
if (cfg.PB) {this.PB = new B5500Processor("B", this)};
|
||||
if (cfg.PA) {this.PA = new B5500Processor("A", this)}
|
||||
if (cfg.PB) {this.PB = new B5500Processor("B", this)}
|
||||
|
||||
// Determine P1/P2
|
||||
this.PB1L = (cfg.PB1L ? 1 : 0);
|
||||
|
||||
// Configure the I/O Units
|
||||
if (cfg.IO1) {this.IO1 = new B5500IOUnit("1", this)};
|
||||
if (cfg.IO2) {this.IO2 = new B5500IOUnit("2", this)};
|
||||
if (cfg.IO3) {this.IO3 = new B5500IOUnit("3", this)};
|
||||
if (cfg.IO4) {this.IO4 = new B5500IOUnit("4", this)};
|
||||
if (cfg.IO1) {this.IO1 = new B5500IOUnit("1", this)}
|
||||
if (cfg.IO2) {this.IO2 = new B5500IOUnit("2", this)}
|
||||
if (cfg.IO3) {this.IO3 = new B5500IOUnit("3", this)}
|
||||
if (cfg.IO4) {this.IO4 = new B5500IOUnit("4", this)}
|
||||
|
||||
// Configure memory
|
||||
for (x=0; x<8; x++) {
|
||||
|
||||
@@ -644,7 +644,7 @@ B5500IOUnit.prototype.initiateDiskIO = function(u) {
|
||||
} else {
|
||||
this.Daddress++; // bump memory address past the seg address word
|
||||
w = this.W; // convert address word to binary
|
||||
for (x=0; x<7; x++) {
|
||||
for (x=0; x<7; x++) { // 7 decimal digits: 1 for EU, 6 for EU-relative address
|
||||
c = w % 0x40; // get low-order six bits of word
|
||||
segAddr += (c % 0x10)*p; // isolate the numeric portion and accumulate
|
||||
w = (w-c)/0x40; // shift word right six bits
|
||||
@@ -716,7 +716,6 @@ B5500IOUnit.prototype.initiatePrinterIO = function(u) {
|
||||
/**************************************/
|
||||
B5500IOUnit.prototype.forkIO = function forkIO() {
|
||||
/* Asynchronously initiates an I/O operation on this I/O Unit for a peripheral device */
|
||||
var addr; // memory address
|
||||
var chars; // I/O memory transfer length
|
||||
var index; // unit index
|
||||
var u; // peripheral unit object
|
||||
@@ -746,6 +745,7 @@ B5500IOUnit.prototype.forkIO = function forkIO() {
|
||||
} else {
|
||||
this.cc.setUnitBusy(index, 1);
|
||||
u = this.cc.unit[index];
|
||||
|
||||
switch(this.Dunit) {
|
||||
// disk designates
|
||||
case 6:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
605
tools/B5500DiskDirList.html
Normal file
605
tools/B5500DiskDirList.html
Normal file
@@ -0,0 +1,605 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>B5500 Disk Directory List 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 B5500DiskDirList.html
|
||||
************************************************************************
|
||||
* Copyright (c) 2013, Paul Kimpel.
|
||||
* Licensed under the MIT License,
|
||||
* see http://www.opensource.org/licenses/mit-license.php
|
||||
************************************************************************
|
||||
* B5500 Disk Directory List Utility.
|
||||
*
|
||||
* This script opens an IndexedDB database in the browser and attempts to
|
||||
* treat it as a B5500 disk image, listing all of the files and their attributes
|
||||
* in the disk directory.
|
||||
************************************************************************
|
||||
* 2013-04-16 P.Kimpel
|
||||
* Original version, from B5500ColdLoader.html.
|
||||
***********************************************************************/
|
||||
"use strict";
|
||||
|
||||
window.onload = 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 bit(word, bit) {
|
||||
/* Extracts and returns the specified bit from the word */
|
||||
var e = 47-bit; // word lower power exponent
|
||||
var p; // bottom portion of word power of 2
|
||||
|
||||
if (e > 0) {
|
||||
return ((word - word % (p = pow2[e]))/p) % 2;
|
||||
} else {
|
||||
return word % 2;
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
function fieldIsolate(word, start, width) {
|
||||
/* Extracts a bit field [start:width] from word and returns the field */
|
||||
var le = 48-start-width; // lower power exponent
|
||||
var p; // bottom portion of word power of 2
|
||||
|
||||
return (le == 0 ? word : (word - word % (p = pow2[le]))/p) % pow2[width];
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
function spout(text) {
|
||||
/* Appends "text"+NL as a new text node to the panel DOM element */
|
||||
var e = document.createTextNode(text + "\n");
|
||||
|
||||
panel.appendChild(e);
|
||||
$$("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 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 formatDirectoryEntry(body, mfid, fid, header) {
|
||||
/* Formats the disk header for a file */
|
||||
var row = document.createElement("tr");
|
||||
var rx;
|
||||
var text = "";
|
||||
|
||||
function appendCell(row, v, className) {
|
||||
var cell = document.createElement("td");
|
||||
|
||||
if (className && className.search(/\S/) >= 0) {
|
||||
cell.className = className;
|
||||
}
|
||||
cell.appendChild(document.createTextNode(v.toString()));
|
||||
row.appendChild(cell);
|
||||
}
|
||||
|
||||
appendCell(row, mfid.substring(1));
|
||||
appendCell(row, fid.substring(1));
|
||||
appendCell(row, header.recordLength, "rj");
|
||||
appendCell(row, header.blockLength, "rj");
|
||||
appendCell(row, header.recordsPerBlock, "rj");
|
||||
appendCell(row, header.segmentsPerBlock, "rj");
|
||||
appendCell(row, header.logCreationDate);
|
||||
appendCell(row, header.logCreationTime);
|
||||
appendCell(row, header.lastAccessDate);
|
||||
appendCell(row, header.creationDate);
|
||||
appendCell(row, header.fileClass, "rj");
|
||||
appendCell(row, header.fileType, "rj");
|
||||
appendCell(row, header.recordCount, "rj");
|
||||
appendCell(row, header.segmentsPerRow, "rj");
|
||||
appendCell(row, header.maxRows, "rj");
|
||||
|
||||
for (rx=0; rx<header.maxRows; rx++) {
|
||||
text += header.rowAddress[rx].toFixed(0) + " ";
|
||||
}
|
||||
|
||||
appendCell(row, text);
|
||||
body.appendChild(row);
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function directoryList(successor) {
|
||||
/* Reads the existing directory structure to generate a list of the files
|
||||
in the directory. When finished, calls the "successor" function */
|
||||
var block = new Array(480);
|
||||
var body = $$("DirListBody");
|
||||
|
||||
function reportDirBlock(addr, block) {
|
||||
/* Lists the entries in the resulting block; if not the last block,
|
||||
advances to the next block */
|
||||
var atEnd = false;
|
||||
var bx;
|
||||
var fid;
|
||||
var mfid;
|
||||
var namex;
|
||||
|
||||
// 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 -- format its header
|
||||
mfid = wordsToString(block, bx, 1, true);
|
||||
fid = wordsToString(block, bx+1, 1, true);
|
||||
bx = namex*30;
|
||||
formatDirectoryEntry(body, mfid, fid, readDiskHeader(block.slice(bx, bx+30)));
|
||||
}
|
||||
}
|
||||
if (atEnd) {
|
||||
successor();
|
||||
} else {
|
||||
readDiskBlock(addr+16, 16, block, reportDirBlock);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***** outer block of directoryList *****/
|
||||
|
||||
while (body.firstChild) {
|
||||
body.removeChild(body.firstChild);
|
||||
}
|
||||
|
||||
if (!config.EU0) {
|
||||
alert("No EU0 in disk configuration -- cannot load");
|
||||
} else {
|
||||
readDiskBlock(directoryTop+4, 16, block, reportDirBlock);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
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() {
|
||||
directoryList(function() {});
|
||||
});
|
||||
}
|
||||
}
|
||||
</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 List Utility
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id=DirListTable border=1 cellspacing=0 cellpadding=1>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>MFID
|
||||
<th>FID
|
||||
<th>REC
|
||||
<th>BLK
|
||||
<th>RPB
|
||||
<th>SPB
|
||||
<th>LCD
|
||||
<th>LCT
|
||||
<th>LAD
|
||||
<th>CRD
|
||||
<th>FCL
|
||||
<th>FTY
|
||||
<th>CNT
|
||||
<th>SPR
|
||||
<th>MXR
|
||||
<th>Row Addresses
|
||||
<tbody id=DirListBody>
|
||||
</table>
|
||||
|
||||
<pre id=TextPanel>
|
||||
</pre>
|
||||
|
||||
<div id=PageBottom>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
772
tools/B5500DiskFileList.html
Normal file
772
tools/B5500DiskFileList.html
Normal file
@@ -0,0 +1,772 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>B5500 Disk File List 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 B5500DiskFileList.html
|
||||
************************************************************************
|
||||
* Copyright (c) 2013, Paul Kimpel.
|
||||
* Licensed under the MIT License,
|
||||
* see http://www.opensource.org/licenses/mit-license.php
|
||||
************************************************************************
|
||||
* B5500 Disk File List Utility.
|
||||
*
|
||||
* This script opens an IndexedDB database in the browser and attempts to
|
||||
* treat it as a B5500 disk image, listing all of the files and their attributes
|
||||
* in the disk directory, and allowing you to select a specific file and list
|
||||
* it to a sub-window.
|
||||
************************************************************************
|
||||
* 2013-04-17 P.Kimpel
|
||||
* Original version, from B5500DiskDirList.html.
|
||||
***********************************************************************/
|
||||
"use strict";
|
||||
|
||||
window.onload = 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 headers = {};
|
||||
|
||||
var config = null; // copy of CONFIG store contents
|
||||
var disk = null; // the IDB database object
|
||||
var panel = $$("TextPanel");
|
||||
|
||||
var BICtoANSIChar = [ // Index by 6-bit BIC to get ANSI character
|
||||
"0", "1", "2", "3", "4", "5", "6", "7",
|
||||
"8", "9", "#", "@", "?", ":", ">", "}",
|
||||
"+", "A", "B", "C", "D", "E", "F", "G",
|
||||
"H", "I", ".", "[", "&", "(", "<", "~",
|
||||
"|", "J", "K", "L", "M", "N", "O", "P",
|
||||
"Q", "R", "$", "*", "-", ")", ";", "{",
|
||||
" ", "/", "S", "T", "U", "V", "W", "X",
|
||||
"Y", "Z", ",", "%", "!", "=", "]", "\""];
|
||||
|
||||
var 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 bit(word, bit) {
|
||||
/* Extracts and returns the specified bit from the word */
|
||||
var e = 47-bit; // word lower power exponent
|
||||
var p; // bottom portion of word power of 2
|
||||
|
||||
if (e > 0) {
|
||||
return ((word - word % (p = pow2[e]))/p) % 2;
|
||||
} else {
|
||||
return word % 2;
|
||||
}
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
function fieldIsolate(word, start, width) {
|
||||
/* Extracts a bit field [start:width] from word and returns the field */
|
||||
var le = 48-start-width; // lower power exponent
|
||||
var p; // bottom portion of word power of 2
|
||||
|
||||
return (le == 0 ? word : (word - word % (p = pow2[le]))/p) % pow2[width];
|
||||
};
|
||||
|
||||
/**************************************/
|
||||
function spout(text) {
|
||||
/* Appends "text"+NL as a new text node to the panel DOM element */
|
||||
var e = document.createTextNode(text + "\n");
|
||||
|
||||
panel.appendChild(e);
|
||||
$$("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 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 listFile(fileName) {
|
||||
/* Opens a sub-window and lists the specified file as text to that window */
|
||||
var block;
|
||||
var doc;
|
||||
var header = headers[fileName];
|
||||
var htmlMatch = /[<>&"]/g;
|
||||
var recNr = 0;
|
||||
var rowAddr;
|
||||
var rowNr = 0;
|
||||
var rowEnd;
|
||||
var win;
|
||||
|
||||
function htmlFilter(char) {
|
||||
/* Used to escape HTML-sensitive characters in a string */
|
||||
switch (char) {
|
||||
case "&":
|
||||
return "&";
|
||||
case "<":
|
||||
return "<";
|
||||
case ">":
|
||||
return ">";
|
||||
case "\"":
|
||||
return """;
|
||||
default:
|
||||
return char;
|
||||
}
|
||||
}
|
||||
|
||||
function listFileBlock(addr, block) {
|
||||
/* Lists a block of a file, and if appropriate, calls itself for the next block */
|
||||
var text;
|
||||
var x;
|
||||
|
||||
for (x=0; x<header.blockLength; x+=header.recordLength) {
|
||||
text = wordsToString(block, x, header.recordLength, true);
|
||||
text = text.replace(htmlMatch, htmlFilter);
|
||||
doc.writeln(text);
|
||||
if (++recNr > header.recordCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rowAddr += header.segmentsPerBlock;
|
||||
if (recNr > header.recordCount) {
|
||||
rowNr = header.maxRows;
|
||||
listFileRow(listFileBlock);
|
||||
} else {
|
||||
if (rowAddr < rowEnd) {
|
||||
readDiskBlock(rowAddr, header.segmentsPerBlock, block, listFileBlock);
|
||||
} else {
|
||||
rowNr++;
|
||||
listFileRow(listFileBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function listPBDBlock(addr, block) {
|
||||
/* Lists a block of a printer-backup file, and if appropriate, calls itself for the next block */
|
||||
var ctl;
|
||||
var eof;
|
||||
var memInhibit;
|
||||
var logicalRecNr;
|
||||
var skip;
|
||||
var space;
|
||||
var text;
|
||||
var wordCount;
|
||||
var x;
|
||||
|
||||
for (x=header.blockLength-1; x>0; x-=18) {
|
||||
ctl = block[x];
|
||||
logicalRecNr = ctl % 0x8000;
|
||||
skip = (ctl % 0x80000 - ctl % 0x8000)/0x8000;
|
||||
space = (ctl % 0x200000 - ctl % 0x80000)/0x80000;
|
||||
eof = (ctl % 0x10000000 - ctl % 0x8000000)/0x8000000;
|
||||
memInhibit = (ctl % 0x40000000 - ctl % 0x20000000)/0x20000000;
|
||||
wordCount = (ctl % 0x8000000000 - ctl % 0x40000000)/0x40000000;
|
||||
|
||||
if (memInhibit) {
|
||||
text = "";
|
||||
} else {
|
||||
text = rtrim(wordsToString(block, x-17, wordCount, true));
|
||||
text = text.replace(htmlMatch, htmlFilter);
|
||||
}
|
||||
doc.writeln(text); // can't handle space==0 overprinting yet
|
||||
if (skip > 0) {
|
||||
doc.writeln("<hr>");
|
||||
} else if (space & 0x01) {
|
||||
doc.writeln(); // double space after print
|
||||
}
|
||||
if (eof) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rowAddr += header.segmentsPerBlock;
|
||||
recNr++;
|
||||
if (eof || recNr > header.recordCount) {
|
||||
rowNr = header.maxRows;
|
||||
listFileRow(listPBDBlock);
|
||||
} else {
|
||||
if (rowAddr < rowEnd) {
|
||||
readDiskBlock(rowAddr, header.segmentsPerBlock, block, listPBDBlock);
|
||||
} else {
|
||||
rowNr++;
|
||||
listFileRow(listPBDBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function listFileRow(lister) {
|
||||
/* Drives the listing of one row of the file */
|
||||
|
||||
if (rowNr >= header.maxRows) {
|
||||
doc.writeln("</pre>");
|
||||
doc.close();
|
||||
} else {
|
||||
rowAddr = header.rowAddress[rowNr];
|
||||
if (rowAddr) {
|
||||
rowEnd = rowAddr + header.segmentsPerRow - 1;
|
||||
readDiskBlock(rowAddr, header.segmentsPerBlock, block, lister);
|
||||
} else {
|
||||
rowNr++;
|
||||
listFileRow(lister);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
win = window.open("", "FileListWin", "resizable,scrollbars," +
|
||||
",left=" + screen.availWidth*0.10 + ",top=" + screen.availHeight*0.10 +
|
||||
",width=" + screen.availWidth*0.90 + ",height=" + screen.availHeight*0.90);
|
||||
win.focus();
|
||||
doc = win.document;
|
||||
doc.open();
|
||||
|
||||
doc.writeln(fileName);
|
||||
doc.writeln("<br><pre>");
|
||||
doc.title = fileName;
|
||||
|
||||
block = new Array(header.blockLength);
|
||||
if (fileName.indexOf("PBD/") == 0 & header.recordLength == 90 && header.blockLength == 90) {
|
||||
listFileRow(listPBDBlock);
|
||||
} else {
|
||||
listFileRow(listFileBlock);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function formatDirectoryEntry(body, mfid, fid, header) {
|
||||
/* Formats the disk header for a file */
|
||||
var cell;
|
||||
var e;
|
||||
var fileName;
|
||||
var row = document.createElement("tr");
|
||||
var rx;
|
||||
var text = "";
|
||||
|
||||
function appendCell(row, v, className) {
|
||||
var cell = document.createElement("td");
|
||||
|
||||
if (className && className.search(/\S/) >= 0) {
|
||||
cell.className = className;
|
||||
}
|
||||
cell.appendChild(document.createTextNode(v.toString()));
|
||||
row.appendChild(cell);
|
||||
}
|
||||
|
||||
fileName = rtrim(mfid.substring(1)) + "/" + rtrim(fid.substring(1));
|
||||
cell = document.createElement("td");
|
||||
cell.className = "mono";
|
||||
e = document.createElement("a");
|
||||
e.appendChild(document.createTextNode(fileName));
|
||||
e.href = "#";
|
||||
e.addEventListener("click", (function(name) {
|
||||
return function(ev) {ev.preventDefault(); listFile(name)};
|
||||
})(fileName));
|
||||
cell.appendChild(e);
|
||||
row.appendChild(cell);
|
||||
|
||||
appendCell(row, header.recordLength, "rj");
|
||||
appendCell(row, header.blockLength, "rj");
|
||||
appendCell(row, header.recordsPerBlock, "rj");
|
||||
appendCell(row, header.segmentsPerBlock, "rj");
|
||||
appendCell(row, header.logCreationDate);
|
||||
appendCell(row, header.logCreationTime);
|
||||
appendCell(row, header.lastAccessDate);
|
||||
appendCell(row, header.creationDate);
|
||||
appendCell(row, header.fileClass, "rj");
|
||||
appendCell(row, header.fileType, "rj");
|
||||
appendCell(row, header.recordCount, "rj");
|
||||
appendCell(row, header.segmentsPerRow, "rj");
|
||||
appendCell(row, header.maxRows, "rj");
|
||||
body.appendChild(row);
|
||||
|
||||
headers[fileName] = header;
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function directoryList(successor) {
|
||||
/* Reads the existing directory structure to generate a list of the files
|
||||
in the directory. When finished, calls the "successor" function */
|
||||
var block = new Array(480);
|
||||
var body = $$("DirListBody");
|
||||
|
||||
function reportDirBlock(addr, block) {
|
||||
/* Lists the entries in the resulting block; if not the last block,
|
||||
advances to the next block */
|
||||
var atEnd = false;
|
||||
var bx;
|
||||
var fid;
|
||||
var mfid;
|
||||
var namex;
|
||||
|
||||
// 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 -- format its header
|
||||
mfid = wordsToString(block, bx, 1, true);
|
||||
fid = wordsToString(block, bx+1, 1, true);
|
||||
bx = namex*30;
|
||||
formatDirectoryEntry(body, mfid, fid, readDiskHeader(block.slice(bx, bx+30)));
|
||||
}
|
||||
}
|
||||
if (atEnd) {
|
||||
successor();
|
||||
} else {
|
||||
readDiskBlock(addr+16, 16, block, reportDirBlock);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***** outer block of directoryList *****/
|
||||
|
||||
while (body.firstChild) {
|
||||
body.removeChild(body.firstChild);
|
||||
}
|
||||
|
||||
if (!config.EU0) {
|
||||
alert("No EU0 in disk configuration -- cannot load");
|
||||
} else {
|
||||
readDiskBlock(directoryTop+4, 16, block, reportDirBlock);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
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() {
|
||||
directoryList(function() {});
|
||||
});
|
||||
}
|
||||
}
|
||||
</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 File List Utility
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id=DirListTable border=1 cellspacing=0 cellpadding=1>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>File Name
|
||||
<th>REC
|
||||
<th>BLK
|
||||
<th>RPB
|
||||
<th>SPB
|
||||
<th>LCD
|
||||
<th>LCT
|
||||
<th>LAD
|
||||
<th>CRD
|
||||
<th>FCL
|
||||
<th>FTY
|
||||
<th>CNT
|
||||
<th>SPR
|
||||
<th>MXR
|
||||
<tbody id=DirListBody>
|
||||
</table>
|
||||
|
||||
<pre id=TextPanel>
|
||||
</pre>
|
||||
|
||||
<div id=PageBottom>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -180,6 +180,7 @@ WHILE NOT READ(SOURCE, 15, REC) DO 00900900
|
||||
00903200
|
||||
SCANNER; 00903300
|
||||
IF TOKEN ^= 48"01""$" THEN 00903400
|
||||
BEGIN 00903402
|
||||
IF POPCOUNT > 0 THEN 00903405
|
||||
BEGIN 00903410
|
||||
REPLACE PREC BY " " FOR 72, THISSEQNR-1 FOR 8 DIGITS, " " FOR 10; 00903415
|
||||
@@ -191,6 +192,7 @@ WHILE NOT READ(SOURCE, 15, REC) DO 00900900
|
||||
END WHILE; 00903445
|
||||
WRITE(PATCH, 15, PREC); 00903450
|
||||
END; 00903455
|
||||
END 00903456
|
||||
ELSE 00903460
|
||||
BEGIN 00903500
|
||||
SCANNER; 00903600
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>B5500 Coldstart Disk Subsystem Loader</title>
|
||||
<title>B5500 Disk Subsystem Coldstart Loader</title>
|
||||
<meta name="Author" content="Paul Kimpel">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
<meta http-equiv="Content-Script-Type" content="text/javascript">
|
||||
@@ -8,13 +8,13 @@
|
||||
|
||||
<script>
|
||||
/***********************************************************************
|
||||
* retro-b5500/tools B5500ColdLoader.html
|
||||
* retro-b5500/webUI B5500ColdLoader.html
|
||||
************************************************************************
|
||||
* Copyright (c) 2012, Paul Kimpel.
|
||||
* Licensed under the MIT License,
|
||||
* see http://www.opensource.org/licenses/mit-license.php
|
||||
************************************************************************
|
||||
* B5500 Coldstart Disk Subsystem Loader.
|
||||
* B5500 Disk Subsystem Coldstart Loader.
|
||||
*
|
||||
* This script opens an IndexedDB database in the browser (creating it first, if
|
||||
* necessary) and initializes it as a B5500 Head-per-Track disk file subsystem.
|
||||
@@ -108,7 +108,7 @@ window.onload = function() {
|
||||
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,0x23]; // 38-3F, @70-77
|
||||
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
|
||||
@@ -268,6 +268,27 @@ window.onload = function() {
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function ANSItoString(bytes, bx, bLength, asBinary) {
|
||||
/* Translates a portion of an ANSI byte array to a string and returns it.
|
||||
"bytes" = the Uint8Array byte array
|
||||
"bx" = 0-relative offset into "bytes"
|
||||
"bLength" = number of bytes to translate
|
||||
"asBinary" = if truthy, then binary translation is done; otherwise
|
||||
B5500 BCLANSI translation is done */
|
||||
var table = (asBinary ? ANSItoBIC : BCLANSItoBIC);
|
||||
var text = "";
|
||||
var x;
|
||||
|
||||
if (bLength < 0) {
|
||||
bLength = -bLength;
|
||||
}
|
||||
for (x=0; x<bLength; x++) {
|
||||
text += BICtoANSIChar[table[bytes[bx+x]]];
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**************************************/
|
||||
function wordsToANSI(words, wx, wLength, bytes, bx, asBinary) {
|
||||
/* Translates an array of B5500 words to ANSI byte-array format.
|
||||
@@ -925,7 +946,7 @@ window.onload = function() {
|
||||
availBlock = block;
|
||||
}
|
||||
break;
|
||||
} else if (block[bx] == 0x12) { // 0x12=@14, available directory slot
|
||||
} else if (block[bx] == 0x0C) { // 0x0C=@14, available directory slot
|
||||
if (availAddr < 0) {
|
||||
availAddr = addr;
|
||||
availSlot = namex;
|
||||
@@ -1114,6 +1135,7 @@ window.onload = function() {
|
||||
var atEnd = false;
|
||||
var bx;
|
||||
var namex;
|
||||
var rowAddr;
|
||||
var rowMax;
|
||||
var rowSize;
|
||||
var rx;
|
||||
@@ -1124,14 +1146,15 @@ window.onload = function() {
|
||||
if (block[bx] == 0x4C) { // 0x4C=@114, end-of-directory marker
|
||||
atEnd = true;
|
||||
break;
|
||||
} else if (block[bx] != 0x12) { // 0x12=@14, available directory slot
|
||||
} else if (block[bx] != 0x0C) { // 0x0C=@14, available directory slot
|
||||
// Got a live one -- complement its in-use space
|
||||
bx = namex*30;
|
||||
rowSize = block[bx+8];
|
||||
rowMax = fieldIsolate(block[bx+9], 43, 5);
|
||||
for (rx=0; rx<rowMax; rx++) {
|
||||
if (block[bx+10+rx] > 0) {
|
||||
removeAvailable(block[bx+10+rx], rowSize);
|
||||
rowAddr = block[bx+10+rx];
|
||||
if (rowAddr > 0 && rowAddr < 1000000) { // ignore rows not on EU0
|
||||
removeAvailable(rowAddr, rowSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1650,6 +1673,30 @@ window.onload = function() {
|
||||
fileNr++;
|
||||
}
|
||||
|
||||
function createSystemLog(areasize) {
|
||||
/* Creates an empty SYSTEM/LOG file. This uses the same mechanism as enterFile(),
|
||||
so do not call this plus enterFile() more than 15 times */
|
||||
var header = [];
|
||||
var labelx = 240-(fileNr+1)*16;
|
||||
|
||||
stringToANSI(padToLength("SYSTEM", 7), fileLabels, labelx+1);
|
||||
stringToANSI(padToLength("LOG", 7), fileLabels, labelx+9);
|
||||
if (labelx > 15) {
|
||||
stringToANSI("0000001?", fileLabels, labelx-16); // @114, last-entry marker
|
||||
stringToANSI("00000000", fileLabels, labelx-8);
|
||||
}
|
||||
header[0] = 0x0141; // BIC "51" = @0501 = 1 rec/block, 1 seg/block
|
||||
header[3] = 0x1001200; // Date: BIC "00010180" = 1980-01-01
|
||||
header[7] = -1; // currently has no records
|
||||
header[8] = areasize;
|
||||
header[9] = 1; // number of areas
|
||||
header[10] = 0; // row address unallocated
|
||||
wordsToANSI(header, 0, 30, buffer, 0);
|
||||
eu.put(buffer, directoryTop + 18 - fileNr);
|
||||
|
||||
fileNr++;
|
||||
}
|
||||
|
||||
function initializeDirectory(config) {
|
||||
/* Initializes the directory structure on EU0. "config" is the
|
||||
database CONFIG structure */
|
||||
@@ -1721,7 +1768,7 @@ window.onload = function() {
|
||||
// 16: olay core to ECM(AUXMEM)
|
||||
pow2[47-15] + // 15: job core estimates(STATISTICS)
|
||||
// 14: olay data to ECM(AUXMEM)
|
||||
pow2[47-13] + // 13: makes system hang on-should HL msg
|
||||
pow2[47-13] + // 13: makes system hang on should-HL msg
|
||||
// 12: enables datacom(TSS, if not DCP)
|
||||
pow2[47-11] + // 11: library messages for CANDE
|
||||
pow2[47-10] + // 10: ZIP decks to run on batch(SHAREDISK)
|
||||
@@ -1768,7 +1815,8 @@ window.onload = function() {
|
||||
eu.put(buffer, directoryTop);
|
||||
|
||||
// Create a file entry for the system log
|
||||
enterFile("SYSTEM", "LOG", 1, 20000);
|
||||
createSystemLog(20000);
|
||||
/** enterFile("SYSTEM", "LOG", 1, 20000); **/
|
||||
|
||||
// Store the directory labels segment
|
||||
eu.put(fileLabels, directoryTop + 19); // write the directory block file labels
|
||||
@@ -1871,7 +1919,7 @@ window.onload = function() {
|
||||
if (cursor.key-lastKey > 1) {
|
||||
spout("----- " + (cursor.key-lastKey-1) + " unallocated segments -----");
|
||||
}
|
||||
spout(cursor.key + ": " + String.fromCharCode.apply(null, cursor.value));
|
||||
spout(cursor.key + ": " + ANSItoString(cursor.value, 0, cursor.value.length));
|
||||
lastKey = cursor.key;
|
||||
cursor.continue();
|
||||
} else {
|
||||
@@ -1960,6 +2008,8 @@ window.onload = function() {
|
||||
BODY {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: small}
|
||||
TABLE {
|
||||
border-collapse: collapse}
|
||||
TH {
|
||||
vertical-align: bottom}
|
||||
DIV#TapeDirDiv {
|
||||
@@ -1977,7 +2027,8 @@ TBODY#TapeDirBody {
|
||||
|
||||
<div style="position:relative; width:100%; height:3em">
|
||||
<div style="position:absolute; left:0; top:0; width:auto">
|
||||
retro-B5500 Coldstart Disk SubSystem Loader
|
||||
<img src="../webUI/retro-B5500-Logo.png" alt="retro-B5500 Logo" style="float:left">
|
||||
Disk SubSystem Coldstart Loader
|
||||
</div>
|
||||
<div style="text-align:center">
|
||||
<input id=ColdstartBtn type=button value="Cold Start" DISABLED>
|
||||
|
||||
@@ -148,6 +148,10 @@ TABLE#CentralControl {
|
||||
font-size: 10px;
|
||||
font-weight: bold}
|
||||
|
||||
TD#procRate, TD#procSlack {
|
||||
color: white;
|
||||
text-align: right}
|
||||
|
||||
|
||||
.busy {
|
||||
color: white}
|
||||
|
||||
@@ -25,6 +25,8 @@ window.onload = function() {
|
||||
var boundBlinkenlicht;
|
||||
var cc = new B5500CentralControl();
|
||||
var lastBusyMask = 0;
|
||||
var procRate;
|
||||
var procSlack;
|
||||
var timer;
|
||||
var timerInterval = 50; // milliseconds
|
||||
|
||||
@@ -63,12 +65,6 @@ window.onload = function() {
|
||||
cc.halt();
|
||||
$$("HaltBtn").disabled = true;
|
||||
$$("LoadBtn").disabled = false;
|
||||
/*****
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
timer = null;
|
||||
}
|
||||
*****/
|
||||
}
|
||||
|
||||
function LoadBtn_Click(ev) {
|
||||
@@ -111,6 +107,7 @@ window.onload = function() {
|
||||
}
|
||||
|
||||
function dasBlinkenlicht() {
|
||||
var et;
|
||||
var pa = cc.PA;
|
||||
var pb = cc.PB;
|
||||
|
||||
@@ -127,6 +124,9 @@ window.onload = function() {
|
||||
aControl.className = "yellowButton yellowLit";
|
||||
}
|
||||
}
|
||||
et = new Date().getTime()*1000 - pa.procStart;
|
||||
procRate.innerHTML = ((pa.procTime - pa.procStart)/et*100).toFixed(1) + "%";
|
||||
//procSlack.innerHTML = (pa.procSlack/et*100).toFixed(1) + "%";
|
||||
}
|
||||
if (pb) {
|
||||
if (!pb.busy) {
|
||||
@@ -179,6 +179,8 @@ window.onload = function() {
|
||||
aNormal = $$("ANormalBtn");
|
||||
bControl = $$("BControlBtn");
|
||||
bNormal = $$("BNormalBtn");
|
||||
procRate = $$("procRate");
|
||||
procSlack = $$("procSlack");
|
||||
boundBlinkenlicht = bindMethod(dasBlinkenlicht, this);
|
||||
}
|
||||
};
|
||||
@@ -206,7 +208,7 @@ window.onload = function() {
|
||||
<div id=BurroughsLogo>
|
||||
<img id=BurroughsLogoImage src="Burroughs-Logo-Neg.jpg">
|
||||
</div>
|
||||
<div id=B5500Logo> B 5500 </div>
|
||||
<div id=B5500Logo><img src="retro-B5500-Logo.png" alt="retro-B5500 logo"><!-- B 5500 --></div>
|
||||
|
||||
<table id=CentralControl>
|
||||
<colgroup>
|
||||
@@ -233,6 +235,7 @@ window.onload = function() {
|
||||
<td id=CCI15F>DK1F <!-- Disk file #1 read check finished -->
|
||||
<td id=CCI16F>DK2F <!-- Disk file #2 read check finished -->
|
||||
<td colspan=13>
|
||||
<td id=procSlack>
|
||||
<tr id=CCPeripheralRow>
|
||||
<td id=DCA>DCA <!-- 17 -->
|
||||
<td id=PPB>PPB <!-- 18 -->
|
||||
@@ -265,6 +268,7 @@ window.onload = function() {
|
||||
<td id=MTC>MTC <!-- 45 -->
|
||||
<td id=MTB>MTB <!-- 46 -->
|
||||
<td id=MTA>MTA <!-- 47 -->
|
||||
<td id=procRate>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ B5500DiskUnit.prototype.read = function(finish, buffer, length, mode, control) {
|
||||
|
||||
euSize = this.config[euName];
|
||||
if (!euSize) { // EU does not exist
|
||||
finish(this.errorMask | 0x20, 0); // set D27F for EU not ready
|
||||
finish(this.errorMask | 0x20, 0); // set D27F for EU not ready/not present
|
||||
this.errorMask = 0;
|
||||
} else if (segAddr < 0) {
|
||||
finish(this.errorMask | 0x20, 0); // set D27F for invalid starting seg address
|
||||
|
||||
@@ -365,7 +365,7 @@ B5500SPOUnit.prototype.keyDown = function(ev) {
|
||||
result = false;
|
||||
break;
|
||||
case 8: // Backspace
|
||||
setTimeout(that.backspaceChar(), nextTime-stamp);
|
||||
setTimeout(this.backspaceChar, nextTime-stamp);
|
||||
result = false;
|
||||
break;
|
||||
case 13: // Enter
|
||||
|
||||
@@ -712,9 +712,16 @@ function runIt(ev) {
|
||||
stopAddress = 0;
|
||||
}
|
||||
/***************************
|
||||
if (cc.P1.R != 0) {
|
||||
alert("P1.R not zero");
|
||||
stopAddress = 0;
|
||||
if (cc.P1.C < 0x40) {
|
||||
// There is an interrupt
|
||||
switch (cc.P1.C) {
|
||||
case 0x31: // P1 invalid address
|
||||
case 0x32: // P1 stack overflow
|
||||
case 0x3C: // P1 integer overflow
|
||||
alert("Unexpected Processor Interrupt occurred");
|
||||
stopAddress = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
***************************/
|
||||
if (--count <= 0) {
|
||||
@@ -865,6 +872,10 @@ function fileSelector_onChange(ev) {
|
||||
|
||||
function hardwareLoad_onClick(ev) {
|
||||
/* Handle the "Hardware Load" button click */
|
||||
|
||||
cc.clear();
|
||||
cc.P1.clear();
|
||||
cc.load(true);
|
||||
}
|
||||
|
||||
function checkBrowser() {
|
||||
|
||||
BIN
webUI/Burroughs-Meatball.png
Normal file
BIN
webUI/Burroughs-Meatball.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
webUI/retro-B5500-Logo.png
Normal file
BIN
webUI/retro-B5500-Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 744 B |
Reference in New Issue
Block a user