-- LaurelAltoLoader.mesa
-- Edited by Levin, August 18, 1982 10:56 AM
-- Modified by Sandman, August 1, 1980 10:21 AM

DIRECTORY
AltoDefs USING [PagesPerMDS],
AltoFileDefs USING [FP],
BcdDefs USING [FTIndex, FTNull, FTSelf, MTIndex, SGIndex, VersionID],
BcdOps USING [
BcdBase, MTHandle, NameString, ProcessModules, ProcessSegs, SGHandle],
ControlDefs USING [
ControlModule, ControlLink, FrameCodeBase, GFT, GFTIndex, GFTItem,
GlobalFrameHandle, LastAVSlot, MaxNGfi, NullControl, NullGlobalFrame],
DirectoryDefs USING [EnumerateDirectory],
FrameDefs USING [EnterGlobalFrame, EnumerateGlobalFrames],
FrameOps USING [Alloc, CodeHandle, FrameSize, Free, Start],
InlineDefs USING [BITAND],
LoaderOps USING [Binding, BindLink, Load, New],
LoadStateOps USING [Map],
Mopcodes USING [zRBL, zWBL],
SegmentDefs USING [
DataSegmentAddress, DataSegmentHandle, DefaultMDSBase, DeleteFileSegment,
EnumerateFileSegments, FileHandle, FileSegmentAddress, FileSegmentHandle,
FrameDS, HardDown, InsertFile, LongSegmentAddress, MakeDataSegment,
MoveFileSegment, NewFile, NewFileSegment, OldFileOnly, OpenFile, Read,
ReleaseFile, SwapIn, SwapUp, Unlock, VMtoFileSegment],
StringDefs USING [
AppendString, AppendSubString, EquivalentSubStrings, SubString,
SubStringDescriptor],
Storage USING [Node, Free, FreePages, PagesForWords],
Table USING [Base];

LaurelAltoLoader: PROGRAM
IMPORTS
DirectoryDefs, FrameDefs, FrameOps, BcdOps, InlineDefs, LoaderOps,
SegmentDefs, StringDefs, Storage
EXPORTS LoaderOps, FrameDefs =PUBLIC

BEGIN OPEN BcdOps, BcdDefs, ControlDefs;

Binding: PRIVATE TYPE = LoaderOps.Binding;
Map: PRIVATE TYPE = LoadStateOps.Map;
SubStringDescriptor: PRIVATE TYPE = StringDefs.SubStringDescriptor;
SubString: PRIVATE TYPE = StringDefs.SubString;

InvalidFile: PUBLIC SIGNAL [name: STRING] = CODE;

Load: PUBLIC PROCEDURE [name: STRING] RETURNS [bcd: BcdBase] =
BEGIN OPEN SegmentDefs;
file: FileHandle ← NewFile[name, Read, OldFileOnly];
pages: CARDINAL;
bcdseg: FileSegmentHandle ← NewFileSegment[file, 1, 1, Read];
SwapIn[bcdseg];
bcd ← FileSegmentAddress[bcdseg];
pages ← bcd.nPages;
IF bcd.versionIdent # BcdDefs.VersionID OR bcd.definitions THEN
ERROR InvalidFile[
name ! UNWIND => BEGIN Unlock[bcdseg]; DeleteFileSegment[bcdseg] END];
IF pages > 1 THEN
BEGIN
Unlock[bcdseg];
MoveFileSegment[bcdseg, 1, pages];
SwapIn[bcdseg];
bcd ← FileSegmentAddress[bcdseg];
END;
RETURN
END;

LoadConfig: PUBLIC PROCEDURE [name: STRING] RETURNS [PROGRAM] =
BEGIN
cm: ControlModule ← LoaderOps.New[LoaderOps.Load[name], TRUE, FALSE];
RETURN[LOOPHOLE[cm]]
END;

NewConfig: PUBLIC PROCEDURE [name: STRING] =
BEGIN [] ← LoaderOps.New[LoaderOps.Load[name], TRUE, FALSE]; RETURN END;

RunConfig: PUBLIC PROCEDURE [name: STRING] =
BEGIN
cm: ControlDefs.ControlModule ← LoaderOps.New[
LoaderOps.Load[name], TRUE, FALSE];
IF cm # NullControl THEN FrameOps.Start[cm];
RETURN
END;

FileItem: TYPE = POINTER TO FileObject;

FileObject: TYPE = RECORD [
fti: FTIndex, ext: BOOLEAN, handle: SegmentDefs.FileHandle, link: FileItem];

files: FileItem ← NIL;
loadee: BcdBase;
ssb: NameString;
ftb: Table.Base;
nfilestofind: CARDINAL ← 0;
tableopen: BOOLEAN ← FALSE;

FindFiles: PUBLIC PROCEDURE [bcd: BcdBase] =
BEGIN EnterCodeFileNames[loadee]; LookupFileTable[]; END;

EnterCodeFileNames: PROCEDURE [bcd: BcdBase] =
BEGIN

SegSearch: PROCEDURE [sgh: SGHandle, sgi: SGIndex] RETURNS [BOOLEAN] =
BEGIN IF sgh.class = code THEN AddFileName[sgh.file]; RETURN[FALSE]; END;

[] ← BcdOps.ProcessSegs[bcd, SegSearch];
RETURN;
END;

AddFileName: PROCEDURE [file: FTIndex] =
BEGIN
p: FileItem;
i, offset, length: CARDINAL;
FOR p ← files, p.link UNTIL p = NIL DO IF file = p.fti THEN RETURN; ENDLOOP;
p ← Storage.Node[SIZE[FileObject]];
p↑ ← [fti: file, handle: NIL, ext: FALSE, link: files];
files ← p;
IF file = FTSelf THEN
BEGIN p.handle ← SegmentDefs.VMtoFileSegment[loadee].file; RETURN END;
IF file = FTNull THEN BEGIN p.handle ← NIL; RETURN END;
offset ← ftb[file].name;
length ← ssb.size[ftb[file].name];
FOR i IN [offset..offset + length) DO
IF ssb.string.text[i] = ’. THEN BEGIN p.ext ← TRUE; EXIT END; ENDLOOP;
nfilestofind ← nfilestofind + 1;
RETURN;
END;

FindFileName: PROCEDURE [name: SubString, ext: BOOLEAN]
RETURNS [found: BOOLEAN, item: FileItem] =
BEGIN
file: SubStringDescriptor ← [base: @ssb.string, offset:, length:];
FOR item ← files, item.link UNTIL item = NIL DO
file.offset ← ftb[item.fti].name;
file.length ← ssb.size[ftb[item.fti].name];
IF LastCharIsDot[@file] THEN name.length ← name.length + 1;
IF ext = item.ext AND StringDefs.EquivalentSubStrings[@file, name] THEN
RETURN[TRUE, item];
ENDLOOP;
RETURN[FALSE, NIL];
END;

LastCharIsDot: PROCEDURE [name: SubString] RETURNS [BOOLEAN] =
BEGIN RETURN[name.base[name.offset + name.length - 1] = ’.]; END;

FileNotFound: PUBLIC SIGNAL [name: STRING] = CODE;

LookupFileTable: PROCEDURE =
BEGIN
p: FileItem;
ssd: StringDefs.SubStringDescriptor;
name: STRING ← [40];
IF nfilestofind # 0 THEN DirectoryDefs.EnumerateDirectory[CheckOne];
FOR p ← files, p.link UNTIL p = NIL DO
IF p.handle = NIL AND p.fti # FTNull THEN
BEGIN
ssd ←
[base: @ssb.string, offset: ftb[p.fti].name,
length: ssb.size[ftb[p.fti].name]];
name.length ← 0;
StringDefs.AppendSubString[name, @ssd];
IF p.ext THEN StringDefs.AppendString[name, ".bcd"L];
SIGNAL FileNotFound[name];
END;
ENDLOOP;
END;

CheckOne: PROCEDURE [fp: POINTER TO AltoFileDefs.FP, name: STRING]
RETURNS [found: BOOLEAN] =
BEGIN
i: CARDINAL;
dirName: SubStringDescriptor;
bcd: SubStringDescriptor ← [base: "bcd"L, offset: 0, length: 3];
item: FileItem;
FOR i IN [0..name.length) DO
IF name[i] = ’. THEN
BEGIN
IF name.length - i # 5 THEN GOTO UseWholeName;
dirName ← [base: name, offset: i + 1, length: 3];
IF ~StringDefs.EquivalentSubStrings[@dirName, @bcd] THEN
GOTO UseWholeName;
dirName.offset ← 0;
dirName.length ← i;
GOTO HasBCDExtension;
END;
REPEAT
UseWholeName => NULL;
HasBCDExtension =>
BEGIN
[found, item] ← FindFileName[@dirName, FALSE];
IF found THEN RETURN[ThisIsTheOne[fp, item]];
END;
ENDLOOP;
dirName ← [base: name, offset: 0, length: name.length - 1];
-- ignore dot on end
[found, item] ← FindFileName[@dirName, TRUE];
RETURN[IF found THEN ThisIsTheOne[fp, item] ELSE FALSE];
END;

ThisIsTheOne: PROCEDURE [fp: POINTER TO AltoFileDefs.FP, item: FileItem]
RETURNS [BOOLEAN] =
BEGIN
item.handle ← SegmentDefs.InsertFile[fp, SegmentDefs.Read];
nfilestofind ← nfilestofind - 1;
RETURN[nfilestofind = 0];
END;

FileHandleFromTable: PROCEDURE [fti: FTIndex]
RETURNS [file: SegmentDefs.FileHandle] =
BEGIN
p: FileItem;
FOR p ← files, p.link UNTIL p = NIL DO
IF p.fti = fti THEN RETURN[p.handle]; ENDLOOP;
RETURN[NIL];
END;

-- Frame allocation/deallocation


AllocateFrames: PUBLIC PROCEDURE [bcd: BcdBase, alloc, framelinks: BOOLEAN]
RETURNS [POINTER] =
BEGIN OPEN SegmentDefs;
seg: DataSegmentHandle;
IF bcd.nModules = 1 THEN RETURN[AllocateSingleModule[bcd, framelinks]];
seg ← MakeDataSegment[
base: DefaultMDSBase, pages: RequiredFrameSpace[bcd, alloc, framelinks],
info: HardDown];
seg.type ← FrameDS;
RETURN[DataSegmentAddress[seg]];
END;

AllocateSingleModule: PROCEDURE [bcd: BcdBase, framelinks: BOOLEAN]
RETURNS [frame: POINTER] =
BEGIN
size: CARDINAL ← 0;
i: CARDINAL;
mth: MTHandle ← @LOOPHOLE[loadee + loadee.mtOffset, Table.Base][
FIRST[MTIndex]];
framelinks ← framelinks OR mth.links = frame OR ~mth.code.linkspace;
IF framelinks THEN size ← mth.frame.length;
size ← NextMultipleOfFour[size] + mth.framesize;
FOR i IN [0..ControlDefs.LastAVSlot] DO
IF FrameOps.FrameSize[i] >= size THEN BEGIN size ← i; EXIT END; ENDLOOP;
frame ← FrameOps.Alloc[size];
IF framelinks THEN frame ← NextMultipleOfFour[frame + mth.frame.length];
RETURN[frame];
END;

NextMultipleOfFour: PROCEDURE [x: UNSPECIFIED] RETURNS [UNSPECIFIED] =
BEGIN RETURN[x + InlineDefs.BITAND[-x, 3B]]; END;

RequiredFrameSpace: PROCEDURE [bcd: BcdBase, alloc, framelinks: BOOLEAN]
RETURNS [space: CARDINAL] =
BEGIN

FrameSize: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
BEGIN
IF alloc THEN space ← NextMultipleOfFour[space + 1];
IF framelinks OR mth.links = frame OR ~mth.code.linkspace THEN
space ← space + mth.frame.length;
space ← NextMultipleOfFour[space] + mth.framesize;
IF alloc AND ~framelinks AND mth.links = code AND mth.code.linkspace AND
mth.framesize <= 4 THEN space ← space + 3; -- this tries
-- to catch the case where a frame is alloced and framesize <= 4 so
-- it makes it so that enough space is counted so that a small frame
-- will fit.
RETURN[FALSE];
END;

space ← 0;
[] ← BcdOps.ProcessModules[bcd, FrameSize];
RETURN[Storage.PagesForWords[space]];
END;

FindFrameIndex: PUBLIC PROCEDURE [mth: MTHandle, framelinks: BOOLEAN]
RETURNS [fsi: CARDINAL] =
BEGIN
space: CARDINAL ← 0;
IF framelinks THEN space ← mth.frame.length;
space ← NextMultipleOfFour[space] + mth.framesize;
FOR fsi DECREASING IN [0..ControlDefs.LastAVSlot] DO
IF space >= FrameOps.FrameSize[fsi] THEN RETURN[fsi]; ENDLOOP;
RETURN[0]; -- see RequiredFrameSpace for alloced modules w/ framesize<7

END;

GetGfi: PUBLIC PROCEDURE [frame: GlobalFrameHandle, ngfi: [1..MaxNGfi]]
RETURNS [gfi: GFTIndex] =
BEGIN RETURN[FrameDefs.EnterGlobalFrame[frame, ngfi]]; END;

ReleaseFrames: PUBLIC PROCEDURE [
bcd: BcdBase, frames: POINTER, map: LoadStateOps.Map] =
BEGIN
i: CARDINAL;
mtb: Table.Base = LOOPHOLE[bcd + bcd.mtOffset];
IF frames = NIL THEN RETURN;
IF bcd.nModules = 1 THEN
BEGIN

Align: PROCEDURE [POINTER, WORD] RETURNS [POINTER] =
LOOPHOLE[InlineDefs.BITAND];
FrameOps.Free[Align[frames - mtb[FIRST[MTIndex]].frame.length, 177774B]]
END
ELSE Storage.FreePages[frames];
FOR i IN [0..LENGTH[map]) DO
OPEN ControlDefs;
GFT[map[i]] ← GFTItem[frame: NullGlobalFrame, epbase: 0];
ENDLOOP;
END;

-- Code management


FindCode: PUBLIC PROCEDURE [bcd: BcdBase, map: Map] =
BEGIN

GetCode: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
BEGIN

FindShared: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
BEGIN
IF f # frame AND f.code.handle = frame.code.handle THEN
f.shared ← frame.shared ← TRUE;
RETURN[FALSE];
END;

frame: GlobalFrameHandle = ControlDefs.GFT[map[mth.gfi]].frame;
IF ~mth.altoCode THEN InvalidModule[bcd, mth];
frame.code.handle ← FindCodeSegment[bcd, mth, frame];
frame.code.offset ← mth.code.offset;
frame.code.out ← TRUE;
[] ← FrameDefs.EnumerateGlobalFrames[FindShared];
RETURN[FALSE];
END;

[] ← BcdOps.ProcessModules[bcd, GetCode];
RETURN
END;

FindCodeSegment: PUBLIC PROCEDURE [
bcd: BcdBase, mth: MTHandle, frame: GlobalFrameHandle]
RETURNS [seg: SegmentDefs.FileSegmentHandle] =
BEGIN OPEN SegmentDefs;
sgh: SGHandle ← @LOOPHOLE[bcd + bcd.sgOffset, Table.Base][mth.code.sgi];
file: FileHandle;
pages: CARDINAL;

FindSegment: PROCEDURE [s: FileSegmentHandle] RETURNS [BOOLEAN] =
BEGIN RETURN[s.file = file AND s.base = sgh.base AND s.pages = pages]; END;

file ← FileHandleFromTable[sgh.file];
OpenFile[file];
pages ← sgh.pages + sgh.extraPages;
seg ← EnumerateFileSegments[FindSegment];
IF seg = NIL THEN seg ← NewFileSegment[file, sgh.base, pages, Read];
seg.class ← code;
RETURN
END;

BadCode: PUBLIC SIGNAL [name: STRING] = CODE;

InvalidModule: PROCEDURE [bcd: BcdBase, mth: MTHandle] =
BEGIN OPEN SegmentDefs;
name: STRING ← [40];
ssd: StringDefs.SubStringDescriptor ←
[base: @ssb.string, offset: mth.name, length: ssb.size[mth.name]];
StringDefs.AppendSubString[name, @ssd];
SIGNAL BadCode[name];
END;

-- Binding and Map management


InitBinding: PUBLIC PROCEDURE [bcd: BcdBase] RETURNS [binding: Binding] =
BEGIN
i: CARDINAL;
p: POINTER ← Storage.Node[bcd.nDummies*SIZE[LoaderOps.BindLink]];
binding ← DESCRIPTOR[
p - CARDINAL[bcd.firstdummy*SIZE[LoaderOps.BindLink]], bcd.nDummies];
FOR i IN [bcd.firstdummy..bcd.firstdummy + bcd.nDummies) DO
binding[i] ← [whichgfi: 0, body: notbound[]]; ENDLOOP;
END;

ReleaseBinding: PUBLIC PROCEDURE [bcd: BcdBase, binding: Binding]
RETURNS [Binding] =
BEGIN
IF BASE[binding] # NIL THEN
Storage.Free[BASE[binding] + bcd.firstdummy*SIZE[LoaderOps.BindLink]];
RETURN[DESCRIPTOR[NIL, 0]];
END;

InitializeMap: PUBLIC PROCEDURE [bcd: BcdBase] RETURNS [map: LoadStateOps.Map] =
BEGIN
i: CARDINAL;
map ← DESCRIPTOR[Storage.Node[bcd.firstdummy], bcd.firstdummy];
FOR i IN [0..bcd.firstdummy) DO map[i] ← 0; ENDLOOP;
END;

DestroyMap: PUBLIC PROCEDURE [map: LoadStateOps.Map] =
BEGIN IF BASE[map] # NIL THEN Storage.Free[BASE[map]]; END;

-- Link management

ls: POINTER TO ControlDefs.ControlLink;
lls: LONG POINTER TO ControlDefs.ControlLink;
dirty, long: BOOLEAN;

OpenLinkSpace: PROCEDURE [frame: GlobalFrameHandle, mth: MTHandle] =
BEGIN OPEN SegmentDefs;
IF frame.codelinks THEN
BEGIN
seg: FileSegmentHandle ← FrameOps.CodeHandle[frame];
IF seg = NIL THEN ERROR;
SwapIn[seg];
IF (long ← seg.VMpage >= AltoDefs.PagesPerMDS) THEN
lls ← LongSegmentAddress[seg] + mth.code.offset
ELSE ls ← FileSegmentAddress[seg] + mth.code.offset;
END
ELSE BEGIN long ← FALSE; ls ← LOOPHOLE[frame] END;
IF long THEN lls ← lls - mth.frame.length ELSE ls ← ls - mth.frame.length;
dirty ← FALSE;
END;

WriteLink: PROCEDURE [offset: CARDINAL, link: ControlDefs.ControlLink] =
BEGIN
dirty ← TRUE;
IF long THEN WriteLongControlLink[link, lls + offset]
ELSE (ls + offset)↑ ← link;
END;

WriteLongControlLink: PROCEDURE [ControlLink, LONG POINTER] = MACHINE CODE
BEGIN Mopcodes.zWBL, 0 END;

ReadLongControlLink: PROCEDURE [LONG POINTER] RETURNS [ControlLink] = MACHINE
CODE BEGIN Mopcodes.zRBL, 0 END;

ReadLink: PROCEDURE [offset: CARDINAL] RETURNS [link: ControlDefs.ControlLink] =
BEGIN
RETURN[IF long THEN ReadLongControlLink[lls + offset] ELSE (ls + offset)↑];
END;

CloseLinkSpace: PROCEDURE [frame: GlobalFrameHandle] =
BEGIN OPEN SegmentDefs;
seg: FileSegmentHandle ← FrameOps.CodeHandle[frame];
IF frame.codelinks AND seg # NIL THEN
BEGIN
Unlock[seg];
IF dirty THEN BEGIN seg.write ← TRUE; SwapUp[seg]; seg.write ← FALSE; END;
END;
END;

FinalizeUtilities: PUBLIC PROCEDURE =
BEGIN
f: FileItem;
FOR f ← files, files UNTIL f = NIL DO
files ← f.link;
IF f.handle.segcount = 0 THEN SegmentDefs.ReleaseFile[f.handle];
Storage.Free[f];
ENDLOOP;
tableopen ← FALSE;
END;

InitializeUtilities: PUBLIC PROCEDURE [bcd: BcdBase] =
BEGIN
loadee ← bcd;
ssb ← LOOPHOLE[loadee + loadee.ssOffset];
ftb ← LOOPHOLE[loadee + loadee.ftOffset];
IF tableopen THEN FinalizeUtilities[];
tableopen ← TRUE;
END;


END....