-- AltoLoader.mesa Modified by Sandman, August 1, 1980 10:21 AM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY 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, 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]; AltoLoader: 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 fcb: ControlDefs.FrameCodeBase ← frame.code; seg: FileSegmentHandle ← FrameOps.CodeHandle[frame]; fcb.out ← FALSE; IF seg # NIL THEN SwapIn[seg]; IF (long ← frame.code.highByte = 0) THEN lls ← fcb.longbase ELSE ls ← FileSegmentAddress[frame.code.handle] + 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....