-- MakeImageUtilities.Mesa  Edited by Geschke on August 29, 1978  8:44 AM

DIRECTORY
  AltoDefs: FROM "altodefs" USING [PageSize],
  AltoFileDefs: FROM "altofiledefs" USING [DirSN, eofDA, FA, FP, SN],
  BcdMergeDefs: FROM "bcdmergedefs" USING [
    FinalizeMerge, InitializeMerge, MergeBcd, MergedBcdSize, MergeModule,
    WriteMergedBcd],
  CodeDefs: FROM "codedefs" USING [CodeHandle],
  ControlDefs: FROM "controldefs" USING [
    GFT, GFTIndex, GFTItem, NullGlobalFrame],
  DirectoryDefs: FROM "directorydefs" USING [EnumerateDirectory],
  FrameDefs: FROM "framedefs" USING [
    EnumerateGlobalFrames, MakeCodeResident, SwapInCode, SwapOutCode],
  ImageDefs: FROM "imagedefs" USING [ImageHeader, MapItem],
  LoaderBcdUtilDefs: FROM "loaderbcdutildefs" USING [
    BcdBase, ReleaseBcdSeg, SetUpBcd],
  LoadStateDefs: FROM "loadstatedefs" USING [
    BcdAddress, BcdSegFromLoadState, ConfigGFI, ConfigIndex, ConfigNull,
    EnumerateLoadStateBcds, FileSegmentHandle, InitializeRelocation,
    MapRealToConfig, ReleaseRelocation, Relocation],
  MiscDefs: FROM "miscdefs" USING [SetBlock],
  MIUtilityDefs: FROM "miutilitydefs" USING [
    CFA, ConfigIndex, DataSegmentHandle, FileHandle, FileRequest,
    FileSegmentHandle, FP, GlobalFrameHandle, LoadStateGFT, MoveWords,
    PageNumber, ProcDesc, SpaceHeader, vDA],
  SDDefs: FROM "sddefs" USING [SD, sGFTLength],
  SegmentDefs: FROM "segmentdefs" USING [
    AddressFromPage, DataSegmentAddress, DefaultBase, DefaultVersion,
    DeleteDataSegment, EnumerateFileSegments, FileHandle, FileHint,
    FileSegmentHandle, InsertFile, JumpToPage, NewDataSegment, NewFile,
    NewFileSegment, Read, SwapOut, Unlock, Write],
  StreamDefs: FROM "streamdefs" USING [
    DiskHandle, GetIndex, SetIndex, StreamIndex],
  StringDefs: FROM "stringdefs" USING [
    EquivalentString, EquivalentSubStrings, SubStringDescriptor],
  SystemDefs: FROM "systemdefs" USING [AllocatePages, FreePages];

DEFINITIONS FROM ImageDefs, MIUtilityDefs;

MakeImageUtilities: PROGRAM
  IMPORTS CodeDefs, DirectoryDefs, FrameDefs, LoadStateDefs, MiscDefs, SegmentDefs,
    StreamDefs, StringDefs, SystemDefs, BcdMergeDefs, MIUtilityDefs,
    LoaderBcdUtilDefs
  EXPORTS MIUtilityDefs
  SHARES SegmentDefs, ControlDefs, ImageDefs =

PUBLIC BEGIN


-- file requests

  RequestHead: POINTER TO FileRequest;

  InitFileRequest: PROCEDURE = BEGIN  RequestHead ← NIL; END;

  AddFileRequest: PROCEDURE [r: POINTER TO FileRequest] =
    BEGIN
    r.link ← RequestHead;
    RequestHead ← r;
    END;

  DropFileRequest: PROCEDURE [f: FileHandle] =
    BEGIN
    r: POINTER TO FileRequest;
    prev: POINTER TO FileRequest ← NIL;
    FOR r ← RequestHead, r.link UNTIL r = NIL DO
      BEGIN
      IF r.file = f THEN
	IF prev = NIL THEN RequestHead ← r.link
	ELSE prev.link ← r.link;
	EXIT;
	END;
      prev ← r;
      ENDLOOP;
    END;

  ProcessFileRequests: PROCEDURE =
    BEGIN OPEN AltoFileDefs;
    checkone: PROCEDURE [fp: POINTER TO FP, dname: STRING] RETURNS [BOOLEAN] =
      BEGIN
      ss: StringDefs.SubStringDescriptor ← [dname,0,dname.length];
      r: POINTER TO FileRequest;
      prev: POINTER TO FileRequest ← NIL;
      FOR r ← RequestHead, r.link UNTIL r = NIL DO
	IF (WITH r SELECT FROM
	      long => StringDefs.EquivalentSubStrings[@ss,@name],
	      short => StringDefs.EquivalentString[dname,name],
	      ENDCASE => FALSE) THEN
	  BEGIN
	  IF r.file = NIL THEN r.file ← SegmentDefs.InsertFile[fp,r.access]
	  ELSE r.file.fp ← fp↑;
	  IF prev = NIL THEN RequestHead ← r.link
	  ELSE prev.link ← r.link;
	  END
	ELSE prev ← r;
	ENDLOOP;
      RETURN[RequestHead = NIL]
      END;

    DirectoryDefs.EnumerateDirectory[checkone];
    END;

-- bcd file names

GetBcdFileNames: PROCEDURE  [nbcds: ConfigIndex]
RETURNS [names: DESCRIPTOR FOR ARRAY OF STRING] =
  BEGIN
  nfound: ConfigIndex ← 0;
  GetNames: PROCEDURE [fp: POINTER TO FP, s: STRING] RETURNS[BOOLEAN] =
    BEGIN
    FindBcd: PROCEDURE [config: ConfigIndex, bcd: LoadStateDefs.BcdAddress] RETURNS [BOOLEAN] =
      BEGIN
      IF fp↑ = bcd.fp THEN
	BEGIN
	names[config] ← GetString[s];
	nfound ← nfound + 1;
	RETURN[TRUE];
	END;
      RETURN[FALSE];
      END;
    [] ← LoadStateDefs.EnumerateLoadStateBcds[recentfirst, FindBcd];
    RETURN[nfound = nbcds];
    END;
  names ← DESCRIPTOR[GetSpace[nbcds], nbcds];
  MiscDefs.SetBlock[BASE[names], GetString["(anon)"], nbcds];
  DirectoryDefs.EnumerateDirectory[GetNames];
  RETURN[names];
  END;

-- space allocation

SpaceList: POINTER TO SpaceHeader ← NIL;
SpacePointer: POINTER TO SpaceHeader;
SpaceLeft: CARDINAL;

InitSpace: PROCEDURE = BEGIN SpaceLeft ← 0; END;

GetSpace: PROCEDURE [n: CARDINAL] RETURNS [p: POINTER] =
  BEGIN
  newseg: DataSegmentHandle;
  IF n > SpaceLeft THEN
    BEGIN
    newseg ← SegmentDefs.NewDataSegment[SegmentDefs.DefaultBase,1];
    SpacePointer ← SegmentDefs.DataSegmentAddress[newseg];
    SpacePointer.link ← SpaceList;
    SpacePointer.segment ← newseg;
    SpaceList ← SpacePointer;
    SpacePointer ← SpacePointer + SIZE[SpaceHeader];
    SpaceLeft ← AltoDefs.PageSize - SIZE[SpaceHeader];
    END;
  p ← SpacePointer;
  SpacePointer ← SpacePointer + n;
  SpaceLeft ← SpaceLeft - n;
  END;

GetString: PROCEDURE [oldstring: STRING] RETURNS [newstring: STRING] =
  BEGIN
  i, length: CARDINAL;
  string: TYPE = POINTER TO MACHINE DEPENDENT RECORD[length,maxlength: CARDINAL];
  length ← oldstring.length;
  newstring ← GetSpace[(length+5)/2];
  newstring.length ← length;
  LOOPHOLE[newstring, string].maxlength ← length;
  FOR i IN [0..length) DO newstring[i] ← oldstring[i]; ENDLOOP;
  RETURN;
  END;

FreeAllSpace: PROCEDURE =
  BEGIN
  next: POINTER TO SpaceHeader;
  UNTIL SpaceList = NIL DO
    next ← SpaceList.link;
    SegmentDefs.DeleteDataSegment[SpaceList.segment];
    SpaceList ← next;
    ENDLOOP;
  END;

-- image file management

LockCodeSegment: PROCEDURE [p: ProcDesc] =
  BEGIN OPEN FrameDefs;
  frame: GlobalFrameHandle ← ControlDefs.GFT[p.gfi].frame;
  MakeCodeResident[frame];
  SwapInCode[frame];
  SegmentDefs.Unlock[CodeDefs.CodeHandle[frame]];
  END;

UnlockCodeSegment: PROCEDURE [p: ProcDesc] =
  BEGIN
  SegmentDefs.Unlock[CodeDefs.CodeHandle[ControlDefs.GFT[p.gfi].frame]];
  END;

KDSegment: PROCEDURE RETURNS [FileSegmentHandle] =
  BEGIN OPEN SegmentDefs;
  DiskKDFile: FileHandle = NewFile["DiskDescriptor", Read, DefaultVersion];
  FindKD: PROCEDURE [s: FileSegmentHandle] RETURNS [BOOLEAN] =
    BEGIN
    RETURN[s.file = DiskKDFile];
    END;
  RETURN[SegmentDefs.EnumerateFileSegments[FindKD]];
  END;

DAofPage: PROCEDURE [file: FileHandle, page: PageNumber] RETURNS [next: vDA] =
  BEGIN
  cfa: CFA;
  buf: POINTER = SystemDefs.AllocatePages[1];
  cfa.fp ← file.fp;
  cfa.fa ← AltoFileDefs.FA[file.fp.leaderDA,0,0];
  next ← SegmentDefs.JumpToPage[@cfa,page-1,buf].next;
  SystemDefs.FreePages[buf];
  RETURN
  END;

FillInCAs: PROCEDURE [
  Image: POINTER TO ImageHeader, mapindex: CARDINAL, ca: POINTER] =
  BEGIN
  i: CARDINAL;
  map: POINTER TO ARRAY [0..0) OF normal MapItem = LOOPHOLE[@Image.map];
  addr: POINTER;
  FOR i IN [0..mapindex) DO
    addr ← SegmentDefs.AddressFromPage[map[i].page];
    THROUGH [0..map[i].count) DO
      ca↑ ← addr;
      ca ← ca + 1;
      addr ← addr + AltoDefs.PageSize;
      ENDLOOP;
    ENDLOOP;
  END;

SwapOutUnlockedCode: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
  BEGIN
  cseg: FileSegmentHandle ← CodeDefs.CodeHandle[f];
  IF cseg.swappedin AND cseg.lock = 0 THEN FrameDefs.SwapOutCode[f];
  RETURN[FALSE]
  END;

SwapOutUnlocked: PROCEDURE [s: FileSegmentHandle] RETURNS [BOOLEAN] =
  BEGIN
  IF s.lock = 0 THEN SegmentDefs.SwapOut[s];
  RETURN[FALSE];
  END;

BashHint: PROCEDURE [s: FileSegmentHandle] RETURNS [BOOLEAN] =
  BEGIN
  WITH s SELECT FROM
    disk => hint ← SegmentDefs.FileHint[da:AltoFileDefs.eofDA, page:0];
    ENDCASE;
  RETURN[FALSE];
  END;

BashFile: PROCEDURE [f: FileHandle] RETURNS [BOOLEAN] =
  BEGIN OPEN AltoFileDefs;
  f.open ← f.lengthvalid ← FALSE;
  IF f.fp.serial # SN[1,0,0,0,DirSN] THEN
    f.fp ← FP[SN[1,0,1,17777B,177777B],eofDA];
  RETURN[FALSE];
  END;

PatchUpGFT: PROCEDURE =
  BEGIN OPEN ControlDefs;
  i: GFTIndex;
  gft: POINTER TO ARRAY [0..0) OF GFTItem ← GFT;
  FOR i IN [1..SDDefs.SD[SDDefs.sGFTLength]) DO
    IF gft[i] = [frame: NullGlobalFrame, epbase: 177777B] THEN
      gft[i] ← [frame: NullGlobalFrame, epbase: 0];
    ENDLOOP;
  RETURN
  END;

InitLoadStateGFT: PROCEDURE [initgft: LoadStateGFT, merge: BOOLEAN, nbcds: ConfigIndex] =
  BEGIN OPEN ControlDefs, LoadStateDefs;
  rgfi, cgfi: GFTIndex ← 0;
  i: ConfigIndex;
  gft: POINTER TO ARRAY [0..0) OF GFTItem ← GFT;
  gftLength: CARDINAL = SDDefs.SD[SDDefs.sGFTLength];
  MiscDefs.SetBlock[
    p: BASE[initgft], v: ConfigGFI[config: ConfigNull, gfi: 0], l: gftLength];
  IF merge THEN
    FOR rgfi IN [1..gftLength) DO
      IF gft[rgfi].frame # NullGlobalFrame THEN initgft[rgfi] ←
	[config: 0, gfi: (cgfi←cgfi+1)];
      ENDLOOP
  ELSE
    FOR i IN [0..nbcds) DO
      cgfi ← 0;
      FOR rgfi IN [1..gftLength) DO
	IF gft[rgfi].frame # NullGlobalFrame AND
	  LoadStateDefs.MapRealToConfig[rgfi].config = i
	  THEN initgft[rgfi] ← [config: i, gfi: (cgfi←cgfi+1)];
	ENDLOOP;
      ENDLOOP;
  END;

NumberGFIInConfig: PROCEDURE [initgft: LoadStateGFT, con: ConfigIndex]
RETURNS [ngfi: ControlDefs.GFTIndex] =
  BEGIN
  i: ControlDefs.GFTIndex;
  ngfi ← 0;
  FOR i IN [0..LENGTH[initgft]) DO
    IF initgft[i].config = con THEN ngfi ← ngfi + 1;
    ENDLOOP;
  RETURN
  END;

-- Bcd Merging Management

TableSize: CARDINAL = 15*AltoDefs.PageSize;

MergeAllBcds: PROCEDURE [initialgft: LoadStateGFT, code: BOOLEAN,
  names: DESCRIPTOR FOR ARRAY OF STRING] =
  BEGIN OPEN LoadStateDefs, BcdMergeDefs;
  MergeLoadedBcds: PROCEDURE [config: ConfigIndex, addr: BcdAddress] RETURNS [BOOLEAN] =
    BEGIN OPEN LoaderBcdUtilDefs, LoadStateDefs;
    rel: Relocation ← InitializeRelocation[config];
    bcdseg: FileSegmentHandle ← BcdSegFromLoadState[config];
    bcd: BcdBase ← SetUpBcd[bcdseg];
    MergeBcd[bcd, rel, 0, initialgft, code, names[config]];
    ReleaseBcdSeg[bcdseg];
    ReleaseRelocation[rel];
    RETURN [FALSE];
    END;

  MergeCopiedFrames: PROCEDURE [frame: GlobalFrameHandle] RETURNS [BOOLEAN] =
    BEGIN
    copied: GlobalFrameHandle;
    config: ConfigIndex;
    ModuleCopiedFrom: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
      BEGIN
      RETURN [f # frame AND CodeDefs.CodeHandle[f] = CodeDefs.CodeHandle[frame] AND
	(config ← MapRealToConfig[f.gfi].config) # ConfigNull];
      END;
    IF MapRealToConfig[frame.gfi].config # ConfigNull THEN RETURN [FALSE];
    IF (copied←FrameDefs.EnumerateGlobalFrames[ModuleCopiedFrom]) # 
      ControlDefs.NullGlobalFrame THEN
      BEGIN
      MergeModule[frame, copied, initialgft];
      RETURN [FALSE];
      END;
    RETURN [FALSE];
    END;

  InitializeMerge[TableSize, NumberGFIInConfig[initialgft, 0]];
  [] ← EnumerateLoadStateBcds[recentlast, MergeLoadedBcds];
  [] ← FrameDefs.EnumerateGlobalFrames[MergeCopiedFrames];
  [] ← MergedBcdSize[];
  WriteMergedBcd[MoveWords];
  FinalizeMerge[];
  END;

MergeABcd: PROCEDURE [config: ConfigIndex, initgft: LoadStateGFT, code: BOOLEAN,
  names: DESCRIPTOR FOR ARRAY OF STRING] =
  BEGIN OPEN BcdMergeDefs, LoaderBcdUtilDefs, LoadStateDefs;
  rel: Relocation ← InitializeRelocation[config];
  bcdseg: FileSegmentHandle ← BcdSegFromLoadState[config];
  bcd: BcdBase ← SetUpBcd[bcdseg];
  InitializeMerge[bcdseg.pages+1, NumberGFIInConfig[initgft, config]];
  MergeBcd[bcd, rel, config, initgft, code, names[config]];
  ReleaseBcdSeg[bcdseg];
  ReleaseRelocation[rel];
  [] ← MergedBcdSize[];
  WriteMergedBcd[MoveWords];
  FinalizeMerge[];
  END;

NewBcdSegmentFromStream: PROCEDURE [stream: StreamDefs.DiskHandle, page: PageNumber]
  RETURNS [newpage: PageNumber, seg: FileSegmentHandle] =
  BEGIN OPEN SegmentDefs;
  index: StreamDefs.StreamIndex;
  index ← StreamDefs.GetIndex[stream];
  IF index.byte # 0 THEN
    BEGIN
    index.byte ← 0;
    index.page ← index.page + 1;
    StreamDefs.SetIndex[stream, index];
    END;
  seg ← NewFileSegment[stream.file, page+1, index.page-page, Read+Write];
  newpage ← index.page;
  RETURN
  END;

END..