-- Disco.mesa
-- Written by Joe Maleson
-- Last changed by Doug Wyatt, November 6, 1980 2:43 PM
-- DKW: Cache stuff deleted.

DIRECTORY
AltoFileDefs: FROM "AltoFileDefs",
BitBltDefs: FROM "BitBltDefs",
DiscoDefs: FROM "DiscoDefs",
DiskDefs: FROM "DiskDefs",
InlineDefs: FROM "InlineDefs",
MiscDefs: FROM "MiscDefs",
StreamDefs: FROM "StreamDefs",
StringDefs: FROM "StringDefs",
SystemDefs: FROM "SystemDefs";

Disco: PROGRAM
IMPORTS BitBltDefs, DiskDefs, InlineDefs, MiscDefs,
StreamDefs, StringDefs, SystemDefs
EXPORTS DiscoDefs SHARES DiskDefs =
BEGIN

--defs for Scanfile
lSFD: CARDINAL = SIZE[DiscoDefs.SFD];
lBUF: CARDINAL = SIZE[DiscoDefs.BUF];
lCB1: CARDINAL = SIZE[DiskDefs.CB]+1;
-- +1 for queue word!

--defs for lookup
TypeLen: TYPE = MACHINE DEPENDENT RECORD
[type: [0..77B],
len: [0..1777B]
];
FPName: TYPE = MACHINE DEPENDENT RECORD
[ directory,random,nolog: [0..1],
serial1: [0..17777B],
serial2: CARDINAL,
version: CARDINAL,
blank: UNSPECIFIED,
leaderDA: AltoFileDefs.vDA,
ch: PACKED ARRAY [-1..77B] OF CHARACTER
];

ReadPageFromVDA: PUBLIC PROCEDURE [data: POINTER,vda: AltoFileDefs.vDA] =
BEGIN OPEN DiskDefs;
cb: CBptr;
ddc: DDC;
nCBs: CARDINAL = 2; --1 causes fatal error in PrevCB
zn: CBZptr ← SystemDefs.AllocateSegment[SIZE[CBZ]+nCBs*SIZE[CB]];

DiskDefs.InitializeCBstorage[zone: zn,nCBs: nCBs,page: 0,init: clear];
cb ← DiskDefs.GetCB[zone: zn,init: dontClear]; --already cleared
ddc ← [cb: cb,ca: data,da: vda,page: 0,fp: NIL,restore: FALSE,action: ReadLD];
DiskDefs.DoDiskCommand[@ddc];
WHILE cb.status.done = 0 DO ENDLOOP;
IF cb.status.finalStatus # CommandComplete
THEN MiscDefs.CallDebugger["ReadPageFromVDA failed"];
SystemDefs.FreeSegment[zn];
END;
TBlastVDA,TBnextVDA: AltoFileDefs.vDA ← AltoFileDefs.eofDA;
TBdataPage: POINTER TO PACKED ARRAY [0..512) OF [0..377B] ← NIL;
TBzn: DiskDefs.CBZptr ← NIL;

TransferBytes: PUBLIC PROCEDURE[
dp: POINTER TO DiscoDefs.DiskPosition,
nBytes: CARDINAL,
data: POINTER TO PACKED ARRAY [0..0) OF [0..377B],
free: BOOLEAN ← FALSE] =
BEGIN OPEN DiskDefs;
IF dp#NIL THEN {
cb: CBptr;
ddc: DDC;
dataOffset: CARDINAL ← 0;
nCBs: CARDINAL = 2; --1 causes fatal error in PrevCB
thisXfer: CARDINAL ← MIN[nBytes,512-dp.beginByte];
Aligned: BOOLEAN = (dp.beginByte MOD 2) = 0;
dataPage: POINTER TO PACKED ARRAY [0..512) OF [0..377B];
nextVDA: AltoFileDefs.vDA;

IF nBytes > 0 THEN
BEGIN --do transfer
DO --for all pages needed
[nextVDA,dataPage] ← FindCacheEntry[dp.VDA];
IF dataPage = NIL THEN --need to read
BEGIN
IF TBzn = NIL THEN
BEGIN
TBdataPage ← SystemDefs.AllocateSegment[256];
TBzn ← SystemDefs.AllocateSegment[SIZE[CBZ]+nCBs*SIZE[CB]];
DiskDefs.InitializeCBstorage[zone:TBzn,nCBs:nCBs,page:0,init:clear];
END;
cb ← DiskDefs.GetCB[zone: TBzn,init: clear];
ddc←[cb:cb,ca:TBdataPage,da:dp.VDA,page:0,fp:NIL,restore:FALSE,
action: ReadLD];
DiskDefs.DoDiskCommand[@ddc];
WHILE cb.status.done = 0 DO ENDLOOP;
IF cb.status.finalStatus # CommandComplete
THEN MiscDefs.CallDebugger["TransferBytes failed"];
nextVDA ← VirtualDA[cb.labelAddress.next];
dataPage ← MakeCacheEntry[dp.VDA,nextVDA,TBdataPage];
END;

IF data # NIL THEN
BEGIN
IF Aligned THEN InlineDefs.COPY[
to: data+dataOffset/2,
from: dataPage+dp.beginByte/2,
nwords: (thisXfer+1)/2
]
ELSE
BEGIN
destBit: CARDINAL = (dataOffset MOD 2)*8;
srcBit: CARDINAL = (dp.beginByte MOD 2)*8;
x: BitBltDefs.BBTableSpace;
bbt: BitBltDefs.BBptr ← BitBltDefs.AlignedBBTable[@x];
bbt↑ ← [
sourcetype: block, function: replace,
dw: thisXfer*8, dh: 1,
dbmr: 258, sbmr: 258, -- must be even and >256
dlx: destBit, dty: 0, slx: srcBit, sty: 0,
sbca: dataPage+dp.beginByte/2,
dbca: data+dataOffset/2
];
BitBltDefs.BITBLT[bbt];
END;
END;

IF nBytes = thisXfer THEN EXIT;
dataOffset ← dataOffset + thisXfer;
nBytes ← nBytes - thisXfer;
dp.beginByte ← 0;
thisXfer ← MIN[nBytes,512];
dp.VDA ← nextVDA;
ENDLOOP;

dp.beginByte ← (dp.beginByte + nBytes) MOD 512;
IF dp.beginByte = 0 THEN dp.VDA ← nextVDA;
END; --do transfer
};
IF free THEN
BEGIN
SystemDefs.FreeSegment[TBzn];TBzn ← NIL;
SystemDefs.FreeSegment[TBdataPage];TBdataPage ← NIL;
WipeCache[];
END;
END;

-- The cache is now just a single page

WipeCache: PROCEDURE = INLINE {
TBlastVDA ← AltoFileDefs.eofDA;
};

FindCacheEntry: PROCEDURE[thisVDA: AltoFileDefs.vDA]
RETURNS[nextVDA: AltoFileDefs.vDA, data: POINTER] = INLINE {
RETURN[TBnextVDA,IF thisVDA=TBlastVDA THEN TBdataPage ELSE NIL];
};

MakeCacheEntry
: PROCEDURE[
thisVDA,nextVDA: AltoFileDefs.vDA,
buffer: POINTER TO PACKED ARRAY [0..512) OF [0..377B]
] RETURNS[POINTER TO PACKED ARRAY [0..512) OF [0..377B]] = INLINE {
TBlastVDA ← thisVDA;
TBnextVDA ← nextVDA;
IF TBdataPage # buffer THEN ERROR;
RETURN[TBdataPage];
};

IndexFile: PUBLIC PROCEDURE [VDA: AltoFileDefs.vDA,nVDAs: CARDINAL,vDAs: POINTER TO ARRAY [0..0) OF AltoFileDefs.vDA] =
BEGIN OPEN DiskDefs;
currentCb,nextCb: CBptr;
ddc: DDC;
nCBs: CARDINAL = 4;
data: ARRAY [0..256) OF CARDINAL;
zn: CBZptr ← SystemDefs.AllocateSegment[SIZE[CBZ]+nCBs*SIZE[CB]];
pn: CARDINAL;
eof: BOOLEAN ← FALSE;
eofRealDA: DiskDefs.DA = DiskDefs.RealDA[AltoFileDefs.eofDA];

IndexCleanUp: PROCEDURE [cb: DiskDefs.CBptr] =
BEGIN
vDAs[cb.page] ← VirtualDA[cb.header.diskAddress];
IF cb.header.diskAddress = eofRealDA THEN eof←TRUE;
END;

DiskDefs.InitializeCBstorage[zone: zn,nCBs: nCBs,page: 0,init: clear];
currentCb ← DiskDefs.GetCB[zone: zn,init: dontClear]; --already cleared
zn.cleanup ← IndexCleanUp;

FOR pn IN [0..nVDAs) DO
nextCb ← DiskDefs.GetCB[zone: zn,init: clear];
IF eof THEN EXIT;
currentCb.labelAddress ← LOOPHOLE[@nextCb.header.diskAddress]; -- prepare chain
ddc ← [cb: currentCb,ca: @data,da: VDA,page: pn,fp: NIL,
restore: FALSE,action: ReadLD];
DiskDefs.DoDiskCommand[@ddc];
currentCb ← nextCb;
VDA ← AltoFileDefs.fillinDA;
ENDLOOP;

WHILE zn.cbQueue[zn.qHead] # NIL DO [] ← GetCB[zone: zn,init: dontClear]; ENDLOOP;
SystemDefs.FreeSegment[zn];
END;
ScanFile: PUBLIC PROCEDURE [fp: POINTER TO AltoFileDefs.FP, ba: POINTER TO UNSPECIFIED, len: CARDINAL, fa: POINTER TO AltoFileDefs.FA] RETURNS [sfd: POINTER TO DiscoDefs.SFD] =
BEGIN
nb: CARDINAL ← (len-(lSFD+DiskDefs.lCBZ+lCB1))/(lBUF+lCB1);
zone: DiskDefs.CBZptr;
cb: DiskDefs.CBptr;
da: AltoFileDefs.vDA;

IF nb <= 0 THEN MiscDefs.CallDebugger["ScanFile buffer too small"];
MiscDefs.Zero[ba, len];
sfd ← ba+nb*lBUF;
zone ← LOOPHOLE[sfd+lSFD];
DiskDefs.InitializeCBstorage[zone: zone,nCBs: nb,page:0,init: clear];--dontClear];
zone.cleanup ← CleanupCb;
cb ← DiskDefs.GetCB[zone: zone,init: clear];
IF fa # NIL THEN
BEGIN
da ← fa.da;
sfd.next ← fa.page;
END
ELSE da ← fp.leaderDA;

cb.header.diskAddress ← DiskDefs.RealDA[da];
-- initialize DA in first CB
-- Now fill in the SFD
sfd.zone ← zone;
InlineDefs.COPY[to: @sfd.fp,from: fp,nwords: SIZE[AltoFileDefs.FP]];
sfd.nextCb ← cb;
sfd.nb ← nb;
sfd.bufs ← ba;
RETURN[sfd];
END;

CleanupCb: PROCEDURE[cb: DiskDefs.CBptr] =
BEGIN
sfd: POINTER TO DiscoDefs.SFD ← LOOPHOLE[cb.zone-lSFD];
la: POINTER TO DiskDefs.DL ← cb.labelAddress;
nextDA: AltoFileDefs.vDA ← DiskDefs.VirtualDA[la.next];
tail: POINTER TO DiscoDefs.BUF ← cb.dataAddress-3; --offset BUF.data
tail.diskAddress ← DiskDefs.VirtualDA[cb.header.diskAddress];
tail.nchars ← la.bytes;
sfd.tail ← cb.page+1;
IF nextDA = AltoFileDefs.eofDA THEN sfd.next ← 177777B;
END;

nextread: PROCEDURE[sfd: POINTER TO DiscoDefs.SFD] RETURNS[BOOLEAN] =
BEGIN
pn: CARDINAL;
newCB,oldCB: POINTER TO DiskDefs.CB;
ddc: DiskDefs.DDC;
buf: POINTER TO DiscoDefs.BUF;

IF sfd.next = 177777B THEN RETURN[FALSE];
newCB ← sfd.linkCb;
IF newCB = NIL THEN
BEGIN
sfd.linkCb ← DiskDefs.GetCB[sfd.zone,clear!DiskDefs.RetryableDiskError=>CONTINUE];
RETURN[FALSE];
END;
oldCB ← sfd.nextCb;
pn ← sfd.next;
IF pn = 177777B THEN RETURN[FALSE];
oldCB.labelAddress ← LOOPHOLE[@newCB.header.diskAddress];
-- prepare chain
buf ← bufaddr[sfd,pn];
buf.pageNumber ← pn;
ddc←[cb:oldCB,ca:@buf.data,da:AltoFileDefs.fillinDA,page:pn,fp:@sfd.fp,restore: FALSE,action:ReadD];
DiskDefs.DoDiskCommand[@ddc];
--BfsDoDiskCommand(disk, oldCb, lv buf>>BUF.data, fillinDA, lv sfd>>SFD.fp, pn, DCreadD)
sfd.next ← pn+1;
sfd.nextCb ← newCB;
sfd.linkCb ← NIL;
RETURN[TRUE];
END;

ScanBuffer: PUBLIC PROCEDURE[sfd: POINTER TO DiscoDefs.SFD, fa: POINTER TO AltoFileDefs.FA] RETURNS [POINTER TO UNSPECIFIED] =
BEGIN
head: CARDINAL ← sfd.head;
buf: POINTER TO DiscoDefs.BUF;

DO
[] ← nextread[sfd];
IF head < sfd.tail THEN EXIT;
IF sfd.next = 177777B THEN RETURN[NIL];
ENDLOOP;

buf ← bufaddr[sfd,head];
fa.da ← buf.diskAddress;
fa.page ← head;
fa.byte ← buf.nchars;
sfd.head ← head+1;
RETURN [@buf.data];
END;

bufaddr: PROCEDURE[sfd: POINTER TO DiscoDefs.SFD,i: CARDINAL] RETURNS [POINTER TO DiscoDefs.BUF] =
BEGIN
RETURN[sfd.bufs + (i MOD sfd.nb)*lBUF];
END;

ScanFinish: PUBLIC PROCEDURE [sfd: POINTER TO DiscoDefs.SFD] =
BEGIN
zone: DiskDefs.CBZptr ← sfd.zone;
WHILE (zone.queueVec[zone.qHead] # NIL) AND (sfd.next # 177777B) DO
[] ← DiskDefs.GetCB[zone,clear!DiskDefs.RetryableDiskError=>CONTINUE];
ENDLOOP;
END;

Match: PROCEDURE [test,match: STRING] RETURNS [BOOLEAN] =
BEGIN
starTestIndex,starMatchIndex: CARDINAL ← 0;
testIndex,matchIndex: CARDINAL ← 0;
m,t: CHARACTER;

WHILE testIndex < test.length AND matchIndex < match.length DO
m ← match[matchIndex];IF m IN [’A..’Z] THEN m ← m + 40B;
IF m = ’# THEN
BEGIN
testIndex←testIndex+1;matchIndex←matchIndex+1;LOOP;
END;
IF m = ’* THEN
BEGIN
matchIndex←matchIndex+1;IF matchIndex = match.length THEN RETURN[TRUE];
starTestIndex ← testIndex + 1;
starMatchIndex ← matchIndex;
LOOP;
END;
t ← test[testIndex];IF t IN [’A..’Z] THEN t ← t + 40B;
IF m = t THEN
BEGIN
testIndex←testIndex+1;matchIndex←matchIndex+1;LOOP;
END;

--try again
testIndex ← starTestIndex;
IF testIndex = test.length THEN RETURN[FALSE];
starTestIndex ← testIndex+1;
matchIndex ← starMatchIndex;
ENDLOOP;

RETURN[testIndex=test.length AND matchIndex=match.length];
END;

Lookup: PUBLIC PROCEDURE [matchString: STRING] RETURNS [nameValArray: POINTER TO DiscoDefs.LookupList] =
BEGIN
DVHead: TypeLen;
DVBody: FPName;
name: STRING ← [100];
newString: STRING;
firstLinkPointer: POINTER TO DiscoDefs.LookupList ← NIL;
linkPointer: POINTER TO UNSPECIFIED ← @firstLinkPointer;
i: CARDINAL;
BCPLLen: CARDINAL;
mem: POINTER TO DiscoDefs.LookupList;
S: StreamDefs.DiskHandle ← StreamDefs.NewWordStream["sysdir",StreamDefs.Read];
S.reset[S];

UNTIL S.endof[S] DO
DVHead←S.get[S];
[] ← StreamDefs.ReadBlock[S,@DVBody,MIN[SIZE[FPName],DVHead.len-1]];
FOR i IN [SIZE[FPName]..DVHead.len-1) DO [] ← S.get[S];ENDLOOP;
IF DVHead.type = 1 THEN--it’s a file
BEGIN
name.length←0;
BCPLLen ← LOOPHOLE[DVBody.ch[-1],CARDINAL];
FOR i IN [0..BCPLLen) DO
IF DVBody.ch[i] = ’! THEN EXIT;--strip version
StringDefs.AppendChar[name,DVBody.ch[i]];
ENDLOOP;
IF name[name.length-1]=’. THEN name.length←name.length-1;
IF NOT Match[name,matchString] THEN LOOP;
mem ← SystemDefs.AllocateHeapNode[SIZE[DiscoDefs.LookupList]];
newString ← SystemDefs.AllocateHeapString[name.length];
mem.link←NIL;
mem.fp.serial.bits.directory ← DVBody.directory;
mem.fp.serial.bits.random ← DVBody.random;
mem.fp.serial.bits.nolog ← DVBody.nolog;
mem.fp.serial.part1 ← DVBody.serial1;
mem.fp.serial.part2 ← DVBody.serial2;
mem.fp.leaderDA ← DVBody.leaderDA;
mem.name ← newString;
newString.length ← 0;
StringDefs.AppendString[newString,name];
linkPointer↑ ← mem;
linkPointer ← mem;
END;
ENDLOOP;
S.destroy[S];
RETURN[firstLinkPointer];
END;

FreeLookupList: PUBLIC PROCEDURE [p: POINTER TO DiscoDefs.LookupList] =
BEGIN
nextP: POINTER TO DiscoDefs.LookupList;
WHILE p # NIL DO
nextP ← p.link;
SystemDefs.FreeHeapString[p.name];
SystemDefs.FreeHeapNode[p];
p ← nextP;
ENDLOOP;
END;

WipeCache[];
END.