-- BootLoader.mesa -- Last Modified by Sandman, October 7, 1980 9:04 AM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY AltoDefs USING [PageCount, PageSize], BcdDefs USING [ Base, BCD, CTIndex, CTNull, EPIndex, EVNull, Link, MTIndex, UnboundLink, VarLimit, VersionID], BcdOps USING [CTHandle, MTHandle, NameString, ProcessConfigs, ProcessModules], BootmesaOps, CacheOps USING [CopyWrite, READ, WRITE], ControlDefs USING [ AV, ControlModule, EPRange, GFTIndex, GFTItem, GlobalFrame, GlobalFrameHandle, NullControl, NullGlobalFrame, ControlLink, NullLink, UnboundLink], FrameOps USING [MakeFsi], IODefs USING [ CR, NumberFormat, SP, WriteChar, WriteNumber, WriteOctal, WriteString], LoaderOps USING [ CloseLinkSpace, DestroyMap, FindCode, FindFiles, FileNotFound, FinalizeUtilities, InitializeMap, InitializeUtilities, OpenLinkSpace, WriteLink], LoadStateFormat USING [ AltoVersionID, LoadState, LoadStateObject, ModuleInfo, NullModule], LoadStateOps USING [EnterModule, GetModule, Map], MiscDefs USING [Zero], SegmentDefs USING [ Append, DefaultVersion, DeleteFileSegment, FileHandle, FileSegmentAddress, FileSegmentHandle, MoveFileSegment, NewFile, NewFileSegment, OldFileOnly, Read, SwapIn, SwapOut, Unlock, Write], SegOps USING [DefaultBase, NewSeg, Seg, CodeClass], StreamDefs USING [ DisplayHandle, GetDefaultDisplayStream, NewByteStream, StreamHandle], String USING [AppendString], Storage USING [Words, FreeWords, PagesForWords]; BootLoader: PROGRAM IMPORTS CacheOps, BootmesaOps, FrameOps, SegOps, IODefs, BcdOps, MiscDefs, LoaderOps, LoadStateOps, SegmentDefs, StreamDefs, String, Storage EXPORTS BootmesaOps, LoadStateOps = PUBLIC BEGIN OPEN ControlDefs, SegOps, SegmentDefs, BcdDefs; data: POINTER TO BootmesaOps.BootData ← @BootmesaOps.dataObject; BcdBase: TYPE = POINTER TO BcdDefs.BCD; MTHandle: TYPE = BcdOps.MTHandle; Seg: TYPE = SegOps.Seg; GlobalFrameHandle: TYPE = ControlDefs.GlobalFrameHandle; ControlModule: TYPE = ControlDefs.ControlModule; GFTIndex: TYPE = ControlDefs.GFTIndex; loadee: BcdBase; lsseg, initlsseg, fakebcdseg: PUBLIC Seg; InitializeLoading: PUBLIC PROCEDURE [name: STRING] = BEGIN OPEN data, ControlDefs, SegmentDefs, SegOps; bcdseg: FileSegmentHandle ← LoadBcd[ NewFile[name, Read, OldFileOnly] ! InvalidBcd => BEGIN OPEN IODefs; WriteString["Invalid file "L]; WriteString[name]; SIGNAL BootmesaOps.BootAbort; END]; fakebcdseg ← NewSeg[bcdseg.file, bcdseg.base, bcdseg.pages]; fakebcdseg.link2 ← bcdseg; fakebcdseg.resident ← FALSE; fakebcdseg.data ← FALSE; InitLoadStates[]; loadee ← data.bcd ← FileSegmentAddress[bcdseg]; LoaderOps.InitializeUtilities[data.bcd]; SetupModuleTable[]; LoaderOps.FindFiles[ data.bcd ! LoaderOps.FileNotFound => BEGIN OPEN IODefs; WriteString["Cant find a code file "L]; WriteString[name]; SIGNAL BootmesaOps.BootAbort; END]; RETURN END; InvalidBcd: SIGNAL [file: FileHandle] = CODE; LoadBcd: PROCEDURE [bcdfile: FileHandle] RETURNS [bcdseg: FileSegmentHandle] = BEGIN OPEN data, SegmentDefs; pages: AltoDefs.PageCount; bcd: BcdBase; bcdseg ← NewFileSegment[bcdfile, 1, 1, Read]; BootmesaOps.MakeResident[bcdseg]; bcd ← FileSegmentAddress[bcdseg]; IF (pages ← bcd.nPages) # 1 THEN BEGIN Unlock[bcdseg]; MoveFileSegment[bcdseg, 1, pages]; BootmesaOps.MakeResident[bcdseg]; bcd ← FileSegmentAddress[bcdseg]; END; IF bcd.versionIdent # BcdDefs.VersionID OR bcd.definitions THEN ERROR InvalidBcd[ bcdfile ! UNWIND => BEGIN Unlock[bcdseg]; DeleteFileSegment[bcdseg]; END]; END; FinishLoading: PUBLIC PROCEDURE = BEGIN map: LoadStateOps.Map; map ← LoaderOps.InitializeMap[data.bcd]; LoaderOps.FindCode[data.bcd, map]; BootmesaOps.LookUpModules[]; ClassifyCodeSegments[]; AssignControlModules[]; RelocateOnly[data.bcd, map]; LoaderOps.FinalizeUtilities[]; LoaderOps.DestroyMap[map]; SegmentDefs.Unlock[lsseg.link2]; SegmentDefs.SwapOut[lsseg.link2]; END; RelocateOnly: PROCEDURE [bcd: BcdBase, map: LoadStateOps.Map] = BEGIN ModuleSearch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] = BEGIN OPEN data, ControlDefs; i: CARDINAL; gfi: GFTIndex = mth.gfi; frame: GlobalFrameHandle ← moduleTable[gfi].frame; resolved: BOOLEAN ← TRUE; link: Link; info: LoadStateFormat.ModuleInfo; GetVariableLink: PROCEDURE [link: Link] RETURNS [ControlLink] = BEGIN ep: EPIndex; evb: Base ← LOOPHOLE[bcd + bcd.evOffset]; gfi: GFTIndex ← link.vgfi; FindModule: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] = BEGIN IF gfi IN [mth.gfi..mth.gfi + mth.ngfi) THEN BEGIN ep ← VarLimit*(gfi - mth.gfi); RETURN[TRUE] END; RETURN[FALSE] END; mth: MTHandle ← BcdOps.ProcessModules[bcd, FindModule].mth; f: GlobalFrameHandle ← moduleTable[gfi].frame; IF mth = NIL THEN RETURN[ControlDefs.NullLink]; IF (ep ← ep + link.var) = 0 THEN RETURN[LOOPHOLE[f]]; IF mth.variables = EVNull THEN RETURN[ControlDefs.NullLink]; RETURN[LOOPHOLE[f + evb[mth.variables].offsets[ep]]]; END; IF mth.frame.length # 0 THEN BEGIN LoaderOps.OpenLinkSpace[frame, mth]; FOR i IN [0..mth.frame.length) DO link ← mth.frame.frag[i]; SELECT link.vtag FROM proc0, proc1 => IF link.gfi >= bcd.firstdummy THEN BEGIN resolved ← FALSE; LoaderOps.WriteLink[offset: i, link: ControlDefs.UnboundLink] END ELSE LoaderOps.WriteLink[offset: i, link: ConvertLink[link]]; var => IF link.gfi >= bcd.firstdummy THEN BEGIN resolved ← FALSE; LoaderOps.WriteLink[offset: i, link: ControlDefs.NullLink] END ELSE LoaderOps.WriteLink[offset: i, link: GetVariableLink[link]]; type => LoaderOps.WriteLink[offset: i, link: ConvertLink[link]] ENDCASE; ENDLOOP; LoaderOps.CloseLinkSpace[frame]; END; FOR i IN [gfi..gfi + mth.ngfi) DO info ← LoadStateOps.GetModule[i]; info.resolved ← resolved; LoadStateOps.EnterModule[i, info]; ENDLOOP; RETURN[FALSE]; END; [] ← BcdOps.ProcessModules[bcd, ModuleSearch]; RETURN END; ConvertLink: PROCEDURE [bl: Link] RETURNS [cl: ControlLink] = BEGIN IF bl = BcdDefs.UnboundLink THEN RETURN[ControlDefs.UnboundLink]; SELECT bl.vtag FROM proc0, proc1 => cl ← [procedure[gfi: bl.gfi, ep: bl.ep, tag: procedure]]; var => cl ← [procedure[gfi: bl.vgfi, ep: bl.var, tag: frame]]; type => cl ← LOOPHOLE[bl.typeID] ENDCASE; RETURN END; LoadModules: PUBLIC PROCEDURE [which: BootmesaOps.LoadClass] = BEGIN ssb: BcdOps.NameString ← LOOPHOLE[loadee + loadee.ssOffset]; ModuleSearch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] = BEGIN OPEN data, SegmentDefs; i: CARDINAL; frame: GlobalFrameHandle; framelinks: BOOLEAN; gf: GlobalFrame; IF moduleTable[mth.gfi].whenLoaded # which THEN RETURN[FALSE]; framelinks ← mth.links = frame OR ~mth.code.linkspace; frame ← BootmesaOps.AllocGlobalFrame[ mth.framesize, mth.frame.length, framelinks]; SetGFTEntry[frame, mth.gfi, mth.ngfi]; gf ← [gfi: mth.gfi, unused: 0, copied: FALSE, alloced: FALSE, shared: FALSE, started: FALSE, trapxfers: FALSE, codelinks: ~framelinks, global:, code: [offset[offset: mth.code.offset, highHalf: NIL]]]; gf.code.out ← TRUE; CacheOps.CopyWrite[from: @gf, to: frame, size: SIZE[GlobalFrame]]; CacheOps.WRITE[@frame.global[0], ControlDefs.NullControl]; FOR i IN [mth.gfi..mth.gfi + mth.ngfi) DO moduleTable[i].frame ← frame; ENDLOOP; IF Loadmap # NIL THEN BEGIN OPEN IODefs; WriteString["New: g = "L]; WriteNumber[frame, NumberFormat[8, FALSE, TRUE, 6]]; WriteChar[SP]; FOR i IN [mth.name..mth.name + ssb.size[mth.name]) DO WriteChar[ssb.string.text[i]]; ENDLOOP; WriteChar[SP]; WriteChar['[]; WriteOctal[ControlLink[procedure[gfi: mth.gfi, ep: 0, tag: frame]]]; WriteChar[']]; WriteChar[CR]; END; RETURN[FALSE]; END; [] ← BcdOps.ProcessModules[loadee, ModuleSearch]; END; ClassifyCodeSegments: PUBLIC PROCEDURE = BEGIN class: SegOps.CodeClass; MungeFrame: PROCEDURE [gfi: GFTIndex] RETURNS [BOOLEAN] = BEGIN i: CARDINAL; segState: SegOps.CodeClass; FOR i IN [gfi..gfi + data.moduleTable[gfi].mth.ngfi) DO IF (segState ← data.moduleTable[i].code.class) < class THEN data.moduleTable[i].class ← segState ELSE data.moduleTable[i].class ← data.moduleTable[i].code.class ← class; ENDLOOP; RETURN[FALSE]; END; class ← resident; [] ← BootmesaOps.EnumerateResidentModules[MungeFrame]; class ← in; [] ← BootmesaOps.EnumerateSwappedInModules[MungeFrame]; RETURN END; SetupModuleTable: PROCEDURE = BEGIN mt: DESCRIPTOR FOR ARRAY OF BootmesaOps.ModuleInfo; ModuleSearch: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] = BEGIN i: CARDINAL; FOR i IN [mth.gfi..mth.gfi + mth.ngfi) DO mt[i] ← [mth, NIL, NIL, other, FALSE, resident]; ENDLOOP; RETURN[FALSE]; END; mt ← data.moduleTable ← DESCRIPTOR[ Storage.Words[loadee.firstdummy*SIZE[BootmesaOps.ModuleInfo]], loadee.firstdummy]; mt[0] ← [NIL, NIL, NIL, other, FALSE, notLoaded]; [] ← BcdOps.ProcessModules[loadee, ModuleSearch]; RETURN END; CMMapItem: TYPE = RECORD [cti: CTIndex, cm: ControlModule, level: CARDINAL]; AssignControlModules: PROCEDURE = BEGIN OPEN BcdOps, data, ControlDefs; ctb: Base ← LOOPHOLE[loadee+loadee.ctOffset]; mtb: Base ← LOOPHOLE[loadee+loadee.mtOffset]; mt: DESCRIPTOR FOR ARRAY OF BootmesaOps.ModuleInfo = moduleTable; mapIndex, maxLevel: CARDINAL ← 0; cti: CTIndex; i: CARDINAL; cmMap: POINTER TO ARRAY [0..0) OF CMMapItem; cm: ControlModule; MapControls: PROCEDURE [cth: CTHandle, cti: CTIndex] RETURNS [BOOLEAN] = BEGIN OPEN ControlDefs; cm: ControlModule; c: CTIndex; level: CARDINAL ← 0; IF cth.nControls = 0 THEN cm ← NullControl ELSE { cm.list ← Alloc[FrameOps.MakeFsi[cth.nControls + 1 + 1]]; CacheOps.WRITE[@cm.list.nModules, cth.nControls + 1]; FOR i: CARDINAL IN [0..cth.nControls) DO CacheOps.WRITE[ @cm.list.frames[i+1], mt[mtb[cth.controls[i]].gfi].frame]; ENDLOOP; FOR c ← ctb[cti].config, ctb[c].config UNTIL c = CTNull DO level ← level + 1; ENDLOOP; cm.multiple ← TRUE}; cmMap[mapIndex] ← [cti: cti, cm: cm, level: level]; mapIndex ← mapIndex + 1; maxLevel ← MAX[maxLevel, level]; RETURN [FALSE]; END; GetControl: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] = BEGIN OPEN ControlDefs; frame: GlobalFrameHandle ← mt[mth.gfi].frame; IF mth.config # cti THEN RETURN[FALSE]; IF CacheOps.READ[@frame.global[0]] = NullControl THEN CacheOps.WRITE[@frame.global[0], GetLink[cm]]; RETURN [FALSE]; END; IF bcd.nModules = 1 THEN BEGIN frame: GlobalFrameHandle ← mt[1].frame; CacheOps.WRITE[@frame.global[0], NullControl]; RETURN END; cmMap ← Storage.Words[bcd.nConfigs*SIZE[CMMapItem]]; [] ← BcdOps.ProcessConfigs[bcd, MapControls]; FOR level: CARDINAL DECREASING IN [0..maxLevel] DO FOR index: CARDINAL IN [0..mapIndex) DO list, listHead: ControlModule; IF cmMap[index].level # level OR (cm ← cmMap[index].cm) = NullControl THEN LOOP; list ← cm; list.multiple ← FALSE; cti ← cmMap[index].cti; listHead ← SetLink[cm, CacheOps.READ[@list.list.frames[1]]]; CacheOps.WRITE[@list.list.frames[1], listHead]; FOR i: CARDINAL IN [2..ctb[cti].nControls+1) DO CacheOps.WRITE[ @list.list.frames[i], SetLink[GetLink[listHead], CacheOps.READ[@list.list.frames[i]]]]; ENDLOOP; [] ← BcdOps.ProcessModules[bcd, GetControl]; ENDLOOP; ENDLOOP; FOR index: CARDINAL IN [0..mapIndex) DO parent: CARDINAL; list: ControlModule; IF (list ← cmMap[index].cm) = NullControl THEN LOOP; list.multiple ← FALSE; IF (cti ← ctb[cmMap[index].cti].config) = CTNull THEN cm ← NullControl ELSE { FOR parent IN [0..mapIndex) DO IF cmMap[parent].cti = cti THEN EXIT; ENDLOOP; cm ← GetLink[cmMap[parent].cm]}; CacheOps.WRITE[@list.list.frames[0], cm]; ENDLOOP; FOR i IN [0..mapIndex) DO IF ctb[cmMap[i].cti].config = CTNull THEN { cm ← GetLink[cmMap[i].cm]; EXIT}; ENDLOOP; Storage.FreeWords[cmMap]; END; Alloc: PROCEDURE [index: CARDINAL] RETURNS [p: POINTER] = BEGIN DO p ← CacheOps.READ[ControlDefs.AV+index]; IF LOOPHOLE[p, CARDINAL] MOD 4 = 0 THEN EXIT; index ← LOOPHOLE[p, CARDINAL]/4; ENDLOOP; CacheOps.WRITE[ControlDefs.AV+index, CacheOps.READ[p]]; RETURN END; SetLink: PROCEDURE [ cm: ControlModule, frame: GlobalFrameHandle] RETURNS [ControlModule] = { t: ControlModule = CacheOps.READ[@frame.global[0]]; CacheOps.WRITE[@frame.global[0], cm]; RETURN[IF t = ControlDefs.NullControl THEN [frame[frame]] ELSE t]}; GetLink: PROCEDURE [cm: ControlModule] RETURNS [ControlModule] = { list: ControlModule; DO IF ~cm.multiple THEN RETURN[cm]; list ← cm; list.multiple ← FALSE; cm ← CacheOps.READ[@list.list.frames[1]]; ENDLOOP}; loadstate: LoadStateFormat.LoadState; InitLoadStates: PROCEDURE = BEGIN OPEN data, SegmentDefs, SegOps, LoadStateFormat; seg: FileSegmentHandle; pages, i: CARDINAL; swatee: FileHandle; pages ← Storage.PagesForWords[ SIZE[LoadStateObject] + LENGTH[gft]*SIZE[ModuleInfo]]; swatee ← SegmentDefs.NewFile["swatee", Read + Write + Append, DefaultVersion]; lsseg ← NewSeg[swatee, DefaultBase, pages, TRUE]; initlsseg ← NewSeg[swatee, lsseg.base + pages, pages, TRUE]; lsseg.data ← initlsseg.data ← FALSE; seg ← lsseg.link2 ← NewFileSegment[swatee, DefaultBase, pages, Read + Write]; SwapIn[seg]; loadstate ← FileSegmentAddress[seg]; MiscDefs.Zero[loadstate, pages*AltoDefs.PageSize]; loadstate.versionident ← AltoVersionID; loadstate.nBcds ← 1; FOR i IN [0..LENGTH[gft]) DO loadstate.gft[i] ← NullModule; ENDLOOP; RETURN END; EnterModule: PROCEDURE [rgfi: GFTIndex, module: LoadStateFormat.ModuleInfo] = BEGIN loadstate.gft[rgfi] ← module; RETURN END; GetModule: PROCEDURE [rgfi: GFTIndex] RETURNS [module: LoadStateFormat.ModuleInfo] = BEGIN RETURN[loadstate.gft[rgfi]]; END; -- global frame table management gft: PRIVATE DESCRIPTOR FOR ARRAY OF GFTItem; InitializeGFT: PROCEDURE [p: POINTER, l: CARDINAL] = BEGIN gft ← DESCRIPTOR[p, l]; RETURN END; EnumerateGlobalFrames: PROCEDURE [ proc: PROCEDURE [GlobalFrameHandle] RETURNS [BOOLEAN]] RETURNS [GlobalFrameHandle] = BEGIN i: GFTIndex; frame: GlobalFrameHandle; FOR i IN [1..LENGTH[gft]) DO frame ← CacheOps.READ[@gft[i].frame]; IF frame # NullGlobalFrame AND CacheOps.READ[@gft[i].epbase] = 0 AND proc[ frame] THEN RETURN[frame]; ENDLOOP; RETURN[NullGlobalFrame] END; gftrover: PRIVATE CARDINAL ← 0; SetGFTEntry: PROCEDURE [ frame: GlobalFrameHandle, gfi: GFTIndex, ngfi: CARDINAL] = BEGIN i, epoffset: CARDINAL; IF gfi + ngfi >= LENGTH[gft] THEN BootmesaOps.BootmesaError["GFT Full"L]; epoffset ← 0; FOR i IN [gfi..gfi + ngfi) DO LoadStateOps.EnterModule[ rgfi: i, module: [resolved: FALSE, config: 0, gfi: i]]; CacheOps.WRITE[@gft[i].frame, frame]; CacheOps.WRITE[@gft[i].epbase, epoffset]; epoffset ← epoffset + EPRange; ENDLOOP; RETURN END; -- loadmap management Loadmap: PRIVATE StreamDefs.StreamHandle ← NIL; DisplayChar: PRIVATE PROCEDURE [StreamDefs.StreamHandle, CHARACTER]; OpenLoadmap: PROCEDURE [root: STRING] = BEGIN OPEN String; default: StreamDefs.DisplayHandle ← StreamDefs.GetDefaultDisplayStream[]; name: STRING ← [40]; AppendString[name, root]; AppendString[name, ".Loadmap"L]; Loadmap ← StreamDefs.NewByteStream[name, Write + Append]; DisplayChar ← default.put; default.put ← LMput; RETURN END; CloseLoadmap: PROCEDURE = BEGIN default: StreamDefs.DisplayHandle ← StreamDefs.GetDefaultDisplayStream[]; IF default.put # LMput THEN ERROR; default.put ← DisplayChar; Loadmap.destroy[Loadmap]; RETURN END; LMput: PRIVATE PROCEDURE [s: StreamDefs.StreamHandle, c: CHARACTER] = BEGIN DisplayChar[s, c]; Loadmap.put[Loadmap, c]; RETURN END; END....