-- MakeImage.Mesa   Edited by Sandman on  September 24, 1980  10:46 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AllocDefs USING [
    AddSwapStrategy, RemoveSwapStrategy, SwappingProcedure, SwapStrategy],
  AltoDefs USING [BytesPerPage, PageCount, PageNumber, PageSize],
  AltoDisplay USING [DCBchainHead],
  AltoFileDefs USING [CFA, CFP, eofDA, fillinDA, FP, vDA],
  BcdDefs USING [VersionStamp],
  BcdMergeOps USING [MergeBcd],
  BcdOps USING [ProcessModules],
  SymbolOps: FROM "BcdSymbolOps" USING [FindString],
  BFSDefs USING [ActOnPages, GetNextDA],
  ControlDefs USING [
    AllocationVector, AllocationVectorSize, AV, ControlLink, EntryVectorItem,
    FrameHandle, FrameVec, GFT, GFTIndex, GlobalFrameHandle, LargeReturnSlot,
    LastAVSlot, PrefixHandle, ProcDesc, SpecialReturnSlot, StateVector,
    SVPointer],
  CoreSwapDefs USING [level],
  DiskDefs USING [
    DA, DiskPageDesc, DiskRequest, StandardDisk, SwapPages, sysDisk, VirtualDA],
  DiskKDDefs USING [CloseDiskKD, InitializeDiskKD],
  FrameDefs USING [GlobalFrame, SwapOutCode],
  FrameOps USING [
    CodeHandle, FlushLargeFrames, Free, GetReturnLink, MyGlobalFrame,
    MyLocalFrame],
  ImageDefs USING [
    FileRequest, ImageVersion, PuntMesa, StopMesa, UserCleanupProc],
  ImageFormat USING [
    FirstImageDataPage, HeaderPages, ImageHeader, MapItem, MapSpace, VersionID],
  InlineDefs USING [BITAND, COPY],
  LoadStateOps USING [ForceDirty, initstate, ReleaseLoadState, state],
  MiscDefs USING [CurrentTime, GetNetworkNumber, SetBlock, Zero],
  MakeImageOps USING [
    AddFileRequest, BashFile, BashHint, DAofPage, DropFileRequest, FillInCAs,
    FreeAllSpace, InitFileRequest, InitLoadStates, InitSpace, KDSegment,
    LockCodeSegment, ProcessBcds, ProcessFileRequests, UnlockCodeSegment],
  NucleusOps USING [
    BFS, Directory, DiskKD, Faults, Files, FSP, HyperRegion, LoadState, MesaInit,
    Miscellaneous, Modules, NonResident, OurProcess, SegmentsA, SegmentsB,
    StreamsA, StreamsB, StreamsC, StringsA, StringsB],
  OsStaticDefs USING [OsStatics],
  ProcessDefs USING [
    CV, DisableInterrupts, DIW, EnableInterrupts, Priority, SetPriority],
  ProcessOps USING [
    ActiveWord, CurrentPSB, CurrentState, FirstProcess, LastProcess,
    NullQueueHandle, Queue, ReadWDC, ReadyList, Requeue, WakeupsWaiting,
    WriteWDC],
  PSBDefs USING [ProcessHandle, PSB],
  Region USING [Node, NodeObject],
  SDDefs USING [
    sAddFileRequest, sAllocTrap, SD, sGoingAway, sSwapTrap, sXferTrap],
  SegmentDefs USING [
    AddressFromPage, Append, CloseFile, DataSegmentAddress, DataSegmentHandle,
    DefaultMDSBase, DefaultVersion, DeleteDataSegment, EasyDown,
    EnumerateDataSegments, EnumerateFiles, EnumerateFileSegments, FileError,
    FileHandle, FileHint, FileSegmentAddress, FileSegmentHandle, HardDown,
    MakeDataSegment, NewFile, Object, ObjectHandle, Read, ReleaseFile, SetEndOfFile,
    SetFileSegmentDA, SwapOut, Unlock, Write],
  StreamDefs USING [StreamHandle],
  SwapperOps USING [
    AllocateObject, LiberateObject, PositionSeg, mdsNodes, MoveCode, DisableHyperspace,
    EnableHyperspace, InitMemoryConfig],
  Storage USING [Prune, PagesForWords],
  Table USING [Allocate],
  TrapOps USING [ReadATP, ReadOTP, ReadXTS, WriteXTS];

MakeImage: PROGRAM
  IMPORTS
    AllocDefs, BcdMergeOps, BcdOps, SymbolOps, BFSDefs, CoreSwapDefs, DiskDefs,
    DiskKDDefs, FrameDefs, FrameOps, ImageDefs, InlineDefs, LoadStateOps,
    MiscDefs, MakeImageOps, NucleusOps, ProcessDefs, ProcessOps, SegmentDefs,
    SwapperOps, Storage, Table, TrapOps
  EXPORTS ImageDefs
  SHARES ProcessDefs, DiskDefs, SegmentDefs, ControlDefs, ImageDefs =
  BEGIN OPEN DiskDefs, ImageDefs, ControlDefs, SegmentDefs, MakeImageOps;

  CFA: TYPE = AltoFileDefs.CFA;
  DataSegmentHandle: TYPE = SegmentDefs.DataSegmentHandle;
  FP: TYPE = AltoFileDefs.FP;
  FileHandle: TYPE = SegmentDefs.FileHandle;
  FileSegmentHandle: TYPE = SegmentDefs.FileSegmentHandle;
  PageCount: TYPE = AltoDefs.PageCount;
  PageNumber: TYPE = AltoDefs.PageNumber;
  vDA: TYPE = AltoFileDefs.vDA;
  GlobalFrameHandle: TYPE = ControlDefs.GlobalFrameHandle;
  StreamHandle: TYPE = StreamDefs.StreamHandle;
  ProcDesc: TYPE = ControlDefs.ProcDesc;

  SwapTrapDuringMakeImage: PUBLIC SIGNAL = CODE;
  SwapErrorDuringMakeImage: PUBLIC SIGNAL = CODE;
  SwapOutDuringMakeImage: PUBLIC SIGNAL = CODE;
  NoRoomInImageMap: PUBLIC SIGNAL = CODE;

  SwapTrapError: PROCEDURE =
    BEGIN
    dest: ControlDefs.ControlLink;
    s: ControlDefs.StateVector;
    ProcessDefs.DisableInterrupts[];
    s ← STATE;
    dest ← TrapOps.ReadOTP[];
    ProcessDefs.DisableInterrupts[];
    SIGNAL SwapTrapDuringMakeImage;
    RETURN WITH s;
    END;

  SwapOutError: AllocDefs.SwappingProcedure =
    BEGIN SIGNAL SwapOutDuringMakeImage; RETURN[TRUE]; END;

  -- File Segment Transfer Routines

  bufferseg: DataSegmentHandle;
  buffer: POINTER;
  BufferPages: PageCount;

  SwapDR: TYPE = POINTER TO swap DiskRequest;

  TransferPages: PROCEDURE [
    da: vDA, base: PageNumber, pages: PageCount, fp: POINTER TO FP, sdr: SwapDR]
    RETURNS [next: vDA] =
    BEGIN OPEN DiskDefs;
    sdr.da ← @da;
    sdr.firstPage ← base;
    sdr.lastPage ← base + pages - 1;
    sdr.fp ← fp;
    IF SwapPages[sdr].page # base + pages - 1 THEN
      SIGNAL SwapErrorDuringMakeImage;
    next ← sdr.desc.next;
    RETURN[next];
    END;

  TransferFileSegment: PROCEDURE [
    buffer: POINTER, seg: FileSegmentHandle, file: FileHandle, base: PageNumber,
    fileda: vDA] RETURNS [vDA] =
    BEGIN
    dpd: DiskPageDesc;
    sdr: swap DiskRequest;
    old: FileHandle ← seg.file;
    segbase: PageNumber ← seg.base;
    pages: PageCount ← seg.pages;
    segda: vDA;
    WITH s: seg SELECT FROM
      disk => segda ← s.hint.da;
      ENDCASE => ERROR SwapErrorDuringMakeImage;
    seg.base ← base;
    sdr ←
      [ca: buffer, da:, firstPage:, lastPage:, fp:, fixedCA: FALSE, action:,
	lastAction:, signalCheckError: FALSE, option: swap[desc: @dpd]];
    IF seg.swappedin THEN
      BEGIN
      sdr.ca ← AddressFromPage[seg.VMpage];
      sdr.action ← sdr.lastAction ← WriteD;
      fileda ← TransferPages[fileda, base, pages, @file.fp, @sdr];
      old.swapcount ← old.swapcount - 1;
      file.swapcount ← file.swapcount + 1;
      END
    ELSE
      BEGIN
      WHILE BufferPages < pages DO
	pages ← pages - BufferPages;
	sdr.action ← sdr.lastAction ← ReadD;
	segda ← TransferPages[segda, segbase, BufferPages, @old.fp, @sdr];
	sdr.action ← sdr.lastAction ← WriteD;
	fileda ← TransferPages[fileda, base, BufferPages, @file.fp, @sdr];
	segbase ← segbase + BufferPages;
	base ← base + BufferPages;
	ENDLOOP;
      sdr.action ← sdr.lastAction ← ReadD;
      segda ← TransferPages[segda, segbase, pages, @old.fp, @sdr];
      sdr.action ← sdr.lastAction ← WriteD;
      fileda ← TransferPages[fileda, base, pages, @file.fp, @sdr];
      END;
    old.segcount ← old.segcount - 1;
    seg.file ← file;
    WITH s: seg SELECT FROM
      disk => s.hint ← FileHint[AltoFileDefs.eofDA, 0];
      ENDCASE;
    file.segcount ← file.segcount + 1;
    IF old.segcount = 0 THEN ReleaseFile[old];
    RETURN[fileda];
    END;

  EnumerateNeededModules: PROCEDURE [proc: PROCEDURE [GlobalFrameHandle]] =
    BEGIN
    proc[FrameDefs.GlobalFrame[EnumerateNeededModules]];
    proc[FrameDefs.GlobalFrame[MakeImageOps.AddFileRequest]];
    proc[FrameDefs.GlobalFrame[BcdOps.ProcessModules]];
    proc[LOOPHOLE[NucleusOps.BFS]];
    proc[LOOPHOLE[NucleusOps.SegmentsA]];
    proc[LOOPHOLE[NucleusOps.SegmentsB]];
    proc[LOOPHOLE[NucleusOps.Files]];
    proc[LOOPHOLE[NucleusOps.Faults]];
    proc[LOOPHOLE[NucleusOps.DiskKD]];
    proc[LOOPHOLE[NucleusOps.Miscellaneous]];
    proc[LOOPHOLE[NucleusOps.Directory]];
    proc[LOOPHOLE[NucleusOps.StreamsA]];
    proc[LOOPHOLE[NucleusOps.StreamsB]];
    proc[LOOPHOLE[NucleusOps.StreamsC]];
    proc[LOOPHOLE[NucleusOps.FSP]];
    proc[LOOPHOLE[NucleusOps.StringsA]];
    proc[LOOPHOLE[NucleusOps.StringsB]];
    proc[LOOPHOLE[NucleusOps.LoadState]];
    proc[LOOPHOLE[NucleusOps.MesaInit]];
    proc[LOOPHOLE[NucleusOps.NonResident]];
    proc[LOOPHOLE[NucleusOps.OurProcess]];
    proc[LOOPHOLE[NucleusOps.HyperRegion]];
    proc[LOOPHOLE[NucleusOps.Modules]];
    END;

  SwapOutMakeImageCode: PROCEDURE =
    BEGIN OPEN FrameDefs;
    SwapOutCode[GlobalFrame[MakeImageOps.AddFileRequest]];
    SwapOutCode[GlobalFrame[Table.Allocate]];
    SwapOutCode[GlobalFrame[SymbolOps.FindString]];
    SwapOutCode[GlobalFrame[BcdOps.ProcessModules]];
    SwapOutCode[GlobalFrame[LoadStateOps.ReleaseLoadState]];
    SwapOutCode[GlobalFrame[BcdMergeOps.MergeBcd]];
    END;

  InvalidImageName: PUBLIC SIGNAL = CODE;

  ResidentGFI: CARDINAL = 1;

  GetImageFile: PROCEDURE [name: STRING] RETURNS [file: FileHandle] =
    BEGIN OPEN SegmentDefs;
    file ← NewFile[name, Read + Write + Append, DefaultVersion];
    IF file = FrameOps.CodeHandle[GFT[ResidentGFI].frame].file THEN
      SIGNAL InvalidImageName;
    RETURN
    END;

  GetSegment: PROCEDURE [pages: CARDINAL] RETURNS [DataSegmentHandle] =
    BEGIN OPEN SegmentDefs;
    RETURN[MakeDataSegment[base: DefaultMDSBase, pages: pages, info: HardDown]];
    END;

  AssureObjects: PROCEDURE = {
    OPEN SwapperOps, SegmentDefs;
    a: FileHandle;
    b: DataSegmentHandle;
    c: FileSegmentHandle;
    a ← LOOPHOLE[SwapperOps.AllocateObject[SIZE[file Object]]];
    a↑ ← [,file[,,,,,,,,,,,]];
    b ← LOOPHOLE[SwapperOps.AllocateObject[SIZE[data segment Object]]];
    b↑ ← [,segment[,data[,]]];
    c ← LOOPHOLE[SwapperOps.AllocateObject[SIZE[file segment Object]]];
    c↑ ← [,segment[,file[,,,,,,,,]]];
    SwapperOps.LiberateObject[a];
    SwapperOps.LiberateObject[b];
    SwapperOps.LiberateObject[c]};

  InstallImage: PROCEDURE [name: STRING, merge: BOOLEAN] =
    BEGIN OPEN DiskDefs, AltoFileDefs;
    wdc: CARDINAL;
    diskrequest: DiskRequest;
    lpn: PageNumber;
    numChars: CARDINAL;
    savealloctrap, saveswaptrap: ControlLink;
    auxtrapFrame: FrameHandle;
    saveAllocationVector: AllocationVector;
    saveXferTrap, saveXferTrapStatus: UNSPECIFIED;
    nextpage: PageNumber;
    swappedinfilepages, swappedoutfilepages, datapages: PageCount ← 0;
    SwapOutErrorStrategy: AllocDefs.SwapStrategy ← AllocDefs.SwapStrategy[
      link:, proc: SwapOutError];
    mapindex: CARDINAL ← 0;
    maxFileSegPages: CARDINAL ← 0;
    endofdatamapindex: CARDINAL;
    HeaderSeg: DataSegmentHandle;
    Image: POINTER TO ImageFormat.ImageHeader;
    imageDA, HeaderDA: vDA;
    ImageFile: FileHandle;
    diskKD: FileSegmentHandle;
    saveDIW: WORD;
    savePV: ARRAY [0..15] OF UNSPECIFIED;
    saveReadyList: ProcessOps.Queue;
    savePriority: ProcessDefs.Priority;
    saveCurrentPSB: PSBDefs.ProcessHandle;
    saveCurrentState: ControlDefs.SVPointer;
    initstateseg: FileSegmentHandle ← LoadStateOps.initstate;
    stateseg: FileSegmentHandle ← LoadStateOps.state;
    net: CARDINAL ← MiscDefs.GetNetworkNumber[];
    maxbcdsize: CARDINAL;
    creator: BcdDefs.VersionStamp ← ImageDefs.ImageVersion[];
    mdsFreeList: DESCRIPTOR FOR ARRAY OF Region.NodeObject;
    SaveMDSFreeList: PROCEDURE =
      BEGIN OPEN SwapperOps;
      nNodes, i: CARDINAL;
      node: Region.Node;
      nNodes ← 0;
      FOR node ← mdsNodes.fwd, node.fwd UNTIL node = @mdsNodes DO
	nNodes ← nNodes + 1; ENDLOOP;
      IF nNodes = 0 THEN BEGIN mdsFreeList ← DESCRIPTOR[NIL, 0]; RETURN END;
      mdsFreeList ← DESCRIPTOR[auxalloc[nNodes*SIZE[Region.NodeObject]], nNodes];
      i ← 0;
      FOR node ← mdsNodes.fwd, node.fwd UNTIL node = @mdsNodes DO
	mdsFreeList[i] ← node↑; i ← i + 1; ENDLOOP;
      END;
    RestoreMDSFreeList: PROCEDURE =
      BEGIN OPEN SwapperOps;
      i: CARDINAL ← 0;
      node: Region.Node;
      IF LENGTH[mdsFreeList] = 0 THEN RETURN;
      FOR i IN [0..LENGTH[mdsFreeList]) DO
	node ← AddressFromPage[mdsFreeList[i].base];
	node↑ ← mdsFreeList[i];
	ENDLOOP;
      END;
    SaveProcesses: PROCEDURE =
      BEGIN OPEN ProcessOps;
      saveDIW ← ProcessDefs.DIW↑;
      ProcessDefs.DIW↑ ← 0;
      savePV ← ProcessDefs.CV↑;
      WakeupsWaiting↑ ← 0;
      saveReadyList ← ReadyList↑;
      saveCurrentPSB ← CurrentPSB↑;
      savePriority ← CurrentPSB.priority;
      saveCurrentState ← CurrentState↑;
      END;
    RestoreProcesses: PROCEDURE =
      BEGIN OPEN ProcessDefs, ProcessOps;
      p: PSBDefs.ProcessHandle;
      ActiveWord↑ ← 77777B;
      ProcessDefs.DIW↑ ← saveDIW;
      ProcessDefs.CV↑ ← savePV;
      ReadyList↑ ← saveReadyList;
      CurrentPSB↑ ← saveCurrentPSB;
      CurrentPSB.priority ← LAST[Priority];
      CurrentState↑ ← saveCurrentState;
      FOR p ← FirstProcess↑, p + SIZE[PSBDefs.PSB] UNTIL p = LastProcess↑ DO
	IF p.state = alive AND p.timeout # 0 AND p.waitingOnCV THEN
	  BEGIN
	  p.waitingOnCV ← FALSE;
	  Requeue[from: NullQueueHandle, to: ReadyList, p: p];
	  END;
	ENDLOOP;
      SetPriority[savePriority];
      RETURN
      END;
    EnterMapItem: PROCEDURE [vmpage: PageNumber, pages: PageCount] =
      BEGIN OPEN ImageFormat;
      map: POINTER TO ARRAY [0..0) OF normal MapItem = LOOPHOLE[@Image.map];
      IF pages > 127 THEN SIGNAL SwapErrorDuringMakeImage;
      IF mapindex >= MapSpace THEN SIGNAL NoRoomInImageMap;
      map[mapindex] ← MapItem[vmpage, pages, normal[]];
      mapindex ← mapindex + SIZE[normal MapItem];
      END;
    CountFileSegments: PROCEDURE [s: FileSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      IF s # diskKD THEN
	BEGIN
	[] ← SwapperOps.PositionSeg[s, FALSE];
	IF s.swappedin THEN
	  BEGIN
	  swappedinfilepages ← swappedinfilepages + s.pages;
	  IF s.class = code THEN maxFileSegPages ← MAX[maxFileSegPages, s.pages];
	  END
	ELSE
	  BEGIN
	  swappedoutfilepages ← swappedoutfilepages + s.pages;
	  maxFileSegPages ← MAX[maxFileSegPages, s.pages];
	  END
	END;
      RETURN[FALSE];
      END;
    CountDataSegments: PROCEDURE [s: DataSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      IF s # HeaderSeg AND s # bufferseg THEN datapages ← datapages + s.pages;
      RETURN[FALSE];
      END;
    MapDataSegments: PROCEDURE [s: DataSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      IF s # HeaderSeg AND s # bufferseg THEN
	BEGIN EnterMapItem[s.VMpage, s.pages]; nextpage ← nextpage + s.pages; END;
      RETURN[FALSE];
      END;
    WriteSwappedIn: PROCEDURE [s: FileSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      IF s.swappedin THEN
	BEGIN
	imageDA ← TransferFileSegment[buffer, s, ImageFile, nextpage, imageDA];
	EnterMapItem[s.VMpage, s.pages];
	nextpage ← nextpage + s.pages;
	END;
      RETURN[FALSE];
      END;
    WriteSwappedOutCode: PROCEDURE [s: FileSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      IF ~s.swappedin AND s.class = code THEN
	BEGIN
	imageDA ← TransferFileSegment[buffer, s, ImageFile, nextpage, imageDA];
	nextpage ← nextpage + s.pages;
	END;
      RETURN[FALSE];
      END;
    WriteSwappedOutNonCode: PROCEDURE [s: FileSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      IF ~s.swappedin AND s.class # code AND s # diskKD THEN
	BEGIN
	imageDA ← TransferFileSegment[buffer, s, ImageFile, nextpage, imageDA];
	nextpage ← nextpage + s.pages;
	END;
      RETURN[FALSE];
      END;
    SDDefs.SD[SDDefs.sAddFileRequest] ← AddFileRequest;
    ImageFile ← GetImageFile[name];
    diskKD ← KDSegment[];
    ProcessDefs.DisableInterrupts[];
    wdc ← ProcessOps.ReadWDC[];
    CoreSwapDefs.level ← -1;
    SaveProcesses[];
    ImageDefs.UserCleanupProc[Save];
    maxbcdsize ← ProcessBcds[initstateseg, merge];
    [] ← Storage.Prune[];
    SwapperOps.MoveCode[direction: intoMDS];
    SwapperOps.DisableHyperspace[];
    SetupAuxStorage[];
    EnumerateNeededModules[LockCodeSegment];
    HeaderDA ← DAofPage[ImageFile, 1];
    -- [] ← FrameDefs.EnumerateGlobalFrames[SwapOutUnlockedCode];
    -- [] ← EnumerateFileSegments[SwapOutUnlocked];
    -- set up private frame allocation trap
    FrameOps.FlushLargeFrames[];
    savealloctrap ← SDDefs.SD[SDDefs.sAllocTrap];
    SDDefs.SD[SDDefs.sAllocTrap] ← auxtrapFrame ← auxtrap[];
    saveAllocationVector ← AV↑;
    AV↑ ← LOOPHOLE[DataSegmentAddress[AuxSeg], POINTER TO AllocationVector]↑;
    BufferPages ← maxbcdsize + initstateseg.pages;
    bufferseg ← GetSegment[BufferPages];
    HeaderSeg ← GetSegment[1];
    datapages ← 0;
    AssureObjects[];
    [] ← EnumerateDataSegments[CountDataSegments];
    swappedinfilepages ← swappedoutfilepages ← 0;
    [] ← EnumerateFileSegments[CountFileSegments];
    SetEndOfFile[
      ImageFile,
      datapages + swappedinfilepages + swappedoutfilepages +
	ImageFormat.FirstImageDataPage - 1, AltoDefs.BytesPerPage];
    [] ← DiskKDDefs.CloseDiskKD[];
    Image ← DataSegmentAddress[HeaderSeg];
    MiscDefs.Zero[Image, ImageFormat.HeaderPages*AltoDefs.PageSize];
    Image.prefix.versionident ← ImageFormat.VersionID; --Image.prefix.options ← 0;
    --Image.prefix.state.stk[0] ← Image.prefix.state.stk[1] ← 0;
    Image.prefix.state.stkptr ← 2;
    Image.prefix.state.dest ← FrameOps.MyLocalFrame[];
    Image.prefix.type ← makeimage;
    Image.prefix.diskAddresses ← Image;
    Image.prefix.leaderDA ← ImageFile.fp.leaderDA;
    Image.prefix.version ← BcdDefs.VersionStamp[
      time: MiscDefs.CurrentTime[], net: net,
      host: OsStaticDefs.OsStatics.SerialNumber];
    Image.prefix.creator ← creator; -- now disable swapping
    saveswaptrap ← SDDefs.SD[SDDefs.sSwapTrap];
    SDDefs.SD[SDDefs.sSwapTrap] ← SwapTrapError;
    AllocDefs.AddSwapStrategy[@SwapOutErrorStrategy];
    datapages ← 0;
    [] ← EnumerateDataSegments[CountDataSegments];
    swappedinfilepages ← swappedoutfilepages ← 0;
    [] ← EnumerateFileSegments[CountFileSegments];
    nextpage ← ImageFormat.FirstImageDataPage;
    [] ← SegmentDefs.EnumerateDataSegments[MapDataSegments];
    IF nextpage # ImageFormat.FirstImageDataPage + datapages THEN ERROR;
    endofdatamapindex ← mapindex;
    imageDA ← DAofPage[ImageFile, nextpage];
    buffer ← SegmentDefs.DataSegmentAddress[bufferseg];
    [] ← SegmentDefs.EnumerateFileSegments[WriteSwappedIn];
    IF nextpage # ImageFormat.FirstImageDataPage + datapages + swappedinfilepages
      THEN ERROR;
    [] ← SegmentDefs.EnumerateFileSegments[WriteSwappedOutCode];
    [] ← SegmentDefs.EnumerateFileSegments[WriteSwappedOutNonCode];
    SegmentDefs.DeleteDataSegment[bufferseg];
    SegmentDefs.CloseFile[ImageFile ! SegmentDefs.FileError => RESUME ];
    ImageFile.write ← ImageFile.append ← FALSE;
    InitLoadStates[stateseg];
    Image.prefix.loadStateBase ← stateseg.base;
    Image.prefix.initialLoadStateBase ← initstateseg.base;
    Image.prefix.loadStatePages ← initstateseg.pages;
    diskrequest ← DiskRequest[
      ca: auxalloc[datapages + 3], da: auxalloc[datapages + 3], fixedCA: FALSE,
      fp: auxalloc[SIZE[FP]], firstPage: ImageFormat.FirstImageDataPage - 1,
      lastPage: ImageFormat.FirstImageDataPage + datapages - 1, action: WriteD,
      lastAction: WriteD, signalCheckError: FALSE,
      option: update[BFSDefs.GetNextDA]];
    diskrequest.fp↑ ← ImageFile.fp;
    [] ← SegmentDefs.EnumerateFileSegments[BashHint];
    [] ← SegmentDefs.EnumerateFiles[BashFile];
    (diskrequest.ca + 1)↑ ← Image;
    FillInCAs[Image, endofdatamapindex, diskrequest.ca + 2];
    MiscDefs.SetBlock[diskrequest.da, fillinDA, datapages + 3];
    (diskrequest.da + 1)↑ ← HeaderDA;
    SaveMDSFreeList[];
    saveXferTrap ← SDDefs.SD[SDDefs.sXferTrap];
    SDDefs.SD[SDDefs.sXferTrap] ← FrameOps.MyLocalFrame[];
    saveXferTrapStatus ← TrapOps.ReadXTS[];
    [lpn, numChars] ← BFSDefs.ActOnPages[LOOPHOLE[@diskrequest]];
    IF lpn # 0 OR numChars # 0 THEN
      BEGIN
      AltoDisplay.DCBchainHead↑ ← SDDefs.SD[SDDefs.sGoingAway] ← NIL;
      ImageDefs.StopMesa[];
      END;
    ProcessOps.WriteWDC[wdc];
    AV↑ ← saveAllocationVector;
    SDDefs.SD[SDDefs.sAllocTrap] ← savealloctrap;
    SDDefs.SD[SDDefs.sXferTrap] ← saveXferTrap;
    TrapOps.WriteXTS[saveXferTrapStatus];
    SDDefs.SD[SDDefs.sAddFileRequest] ← 0;
    FrameOps.Free[auxtrapFrame];
    RestoreMDSFreeList[];
    SwapperOps.InitMemoryConfig[];
    BootImageFile[ImageFile, LOOPHOLE[Image]];
    DiskDefs.sysDisk ← DiskDefs.StandardDisk;
    DiskKDDefs.InitializeDiskKD[];
    BootPageTable[ImageFile, LOOPHOLE[Image]];
    SegmentDefs.DeleteDataSegment[HeaderSeg]; -- turn swapping back on
    AllocDefs.RemoveSwapStrategy[@SwapOutErrorStrategy];
    SDDefs.SD[SDDefs.sSwapTrap] ← saveswaptrap;
    SwapperOps.EnableHyperspace[];
    CollectDiskAddresses[ImageFile];
    RestoreProcesses[];
    ProcessDefs.EnableInterrupts[];
    ProcessFileRequests[];
    InlineDefs.COPY[
      from: SegmentDefs.FileSegmentAddress[initstateseg],
      to: SegmentDefs.FileSegmentAddress[stateseg],
      nwords: stateseg.pages*AltoDefs.PageSize];
    LoadStateOps.ForceDirty[];
    LoadStateOps.ReleaseLoadState[];
    SegmentDefs.Unlock[initstateseg];
    SegmentDefs.SwapOut[initstateseg];
    SegmentDefs.DeleteDataSegment[AuxSeg];
    FreeAllSpace[];
    EnumerateNeededModules[UnlockCodeSegment];
    SwapperOps.MoveCode[direction: outofMDS];
    SwapOutMakeImageCode[];
    ImageDefs.UserCleanupProc[Restore];
    RETURN
    END;

  -- auxillary storage for frames and non-saved items

  AuxSeg: DataSegmentHandle;
  freepointer: POINTER;
  wordsleft: CARDINAL;
  StoragePages: CARDINAL = 10;

  SetupAuxStorage: PROCEDURE =
    BEGIN
    av: POINTER;
    i: CARDINAL;
    AuxSeg ← GetSegment[StoragePages];
    av ← freepointer ← DataSegmentAddress[AuxSeg];
    wordsleft ← StoragePages*AltoDefs.PageSize;
    [] ← auxalloc[AllocationVectorSize];
    freepointer ← freepointer + 3;
    wordsleft ← wordsleft - 3;
    FOR i IN [0..LastAVSlot] DO (av + i)↑ ← (i + 1)*4 + 2; ENDLOOP;
    (av + 6)↑ ← (av + LargeReturnSlot)↑ ← (av + SpecialReturnSlot + 1)↑ ← 1;
    END;

  auxalloc: PROCEDURE [n: CARDINAL] RETURNS [p: POINTER] =
    BEGIN -- allocate in multiples of 4 words
    p ← freepointer;
    n ← InlineDefs.BITAND[n + 3, 177774B];
    freepointer ← freepointer + n;
    IF wordsleft < n THEN ImageDefs.PuntMesa[];
    wordsleft ← wordsleft - n;
    RETURN
    END;

  auxtrap: PROCEDURE RETURNS [myframe: FrameHandle] =
    BEGIN
    state: StateVector;
    newframe: FrameHandle;
    eventry: POINTER TO EntryVectorItem;
    fsize, findex: CARDINAL;
    newG: GlobalFrameHandle;
    dest, tempdest: ControlLink;
    alloc: BOOLEAN;
    gfi: GFTIndex;
    ep: CARDINAL;
    myframe ← LOOPHOLE[FrameOps.MyLocalFrame[]];
    state.dest ← myframe.returnlink;
    state.source ← 0;
    state.instbyte ← 0;
    state.stk[0] ← myframe;
    state.stkptr ← 1;
    ProcessDefs.DisableInterrupts[];
    DO
      ProcessDefs.EnableInterrupts[];
      TRANSFER WITH state;
      ProcessDefs.DisableInterrupts[];
      state ← STATE;
      dest ← TrapOps.ReadATP[];
      myframe.returnlink ← state.source;
      tempdest ← dest;
      DO
	SELECT tempdest.tag FROM
	  frame =>
	    BEGIN alloc ← TRUE; findex ← LOOPHOLE[tempdest, CARDINAL]/4; EXIT END;
	  procedure =>
	    BEGIN OPEN proc: LOOPHOLE[tempdest, ControlDefs.ProcDesc];
	    gfi ← proc.gfi;
	    ep ← proc.ep;
	    [frame: newG, epbase: findex] ← GFT[gfi];
	    eventry ← @LOOPHOLE[newG.code.shortbase, PrefixHandle].entry[
	      findex + ep];
	    findex ← eventry.info.framesize;
	    alloc ← FALSE;
	    EXIT
	    END;
	  indirect => tempdest ← tempdest.link↑;
	  ENDCASE => ImageDefs.PuntMesa[];
	ENDLOOP;
      IF findex > LastAVSlot THEN ImageDefs.PuntMesa[];
      fsize ← FrameVec[findex];
      IF fsize MOD 4 # 0 THEN fsize ← fsize + 1;
      newframe ← LOOPHOLE[freepointer + 1];
      freepointer↑ ← findex;
      freepointer ← freepointer + fsize;
      IF wordsleft < fsize THEN ImageDefs.PuntMesa[]
      ELSE wordsleft ← wordsleft - fsize;
      IF alloc THEN
	BEGIN
	state.dest ← myframe.returnlink;
	state.stk[state.stkptr] ← newframe;
	state.stkptr ← state.stkptr + 1;
	END
      ELSE
	BEGIN
	state.dest ← dest;
	newframe.accesslink ← LOOPHOLE[AV[findex].frame];
	AV[findex].frame ← newframe;
	state.source ← myframe.returnlink;
	END;
      ENDLOOP;
    END;

  PageTable: TYPE = MACHINE DEPENDENT RECORD [
    fp: AltoFileDefs.CFP,
    firstpage: CARDINAL,
    table: ARRAY [0..1) OF DiskDefs.DA];

  BootImageFile: PROCEDURE [file: FileHandle, pt: POINTER TO PageTable] =
    BEGIN OPEN AltoFileDefs;
    DropFileRequest[file];
    file.open ← TRUE;
    file.fp ← FP[serial: pt.fp.serial, leaderDA: pt.fp.leaderDA];
    RETURN
    END;

  BootPageTable: PROCEDURE [file: FileHandle, pt: POINTER TO PageTable] =
    BEGIN OPEN AltoFileDefs;
    lastpage: PageNumber;
    pageInc: PageNumber = pt.firstpage - ImageFormat.FirstImageDataPage;

    PlugHint: PROCEDURE [seg: FileSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      IF seg.file = file THEN
	BEGIN
	seg.base ← seg.base + pageInc;
	IF seg.base IN [pt.firstpage..lastpage] THEN
	  WITH s: seg SELECT FROM
	    disk =>
	      s.hint ← FileHint[
		page: s.base,
		da: DiskDefs.VirtualDA[pt.table[s.base - pt.firstpage]]];
	    ENDCASE;
	END;
      RETURN[FALSE]
      END;

    FOR lastpage ← 0, lastpage + 1 UNTIL pt.table[lastpage] = DiskDefs.DA[
      0, 0, 0, 0, 0] DO NULL ENDLOOP;
    IF lastpage = 0 THEN RETURN;
    lastpage ← lastpage + pt.firstpage - 1;
    [] ← EnumerateFileSegments[PlugHint];
    RETURN
    END;

  CollectDiskAddresses: PROCEDURE [imageFile: FileHandle] =
    BEGIN OPEN Storage, SegmentDefs, AltoFileDefs;
    DAs: DESCRIPTOR FOR ARRAY OF vDA;
    maxunknown, maxknown: CARDINAL ← FIRST[CARDINAL];
    minunknown: CARDINAL ← LAST[CARDINAL];
    maxknownDA: vDA;
    DisplayHead: POINTER TO WORD = LOOPHOLE[420B];
    saveDisplay: WORD;
    diskrequest: DiskDefs.DiskRequest;
    bufseg, DAseg: DataSegmentHandle;

    FindEnds: PROCEDURE [seg: FileSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      WITH s: seg SELECT FROM
	disk =>
	  IF s.file = imageFile AND s.hint.da = eofDA THEN
	    BEGIN
	    maxunknown ← MAX[maxunknown, s.base];
	    minunknown ← MIN[minunknown, s.base];
	    END;
	ENDCASE;
      RETURN[FALSE];
      END;

    -- of FindEnds --

    FindKnown: PROCEDURE [seg: FileSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      WITH s: seg SELECT FROM
	disk =>
	  IF s.file = imageFile AND s.hint.da # eofDA AND s.base < minunknown AND
	    s.base > maxknown THEN
	    BEGIN maxknown ← s.base; maxknownDA ← s.hint.da END;
	ENDCASE;
      RETURN[FALSE];
      END;

    -- of FindKnown --

    PlugDA: PROCEDURE [seg: FileSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      WITH s: seg SELECT FROM
	disk =>
	  IF s.file = imageFile AND s.hint.da = eofDA AND s.base IN
	    (maxknown..maxunknown] THEN
	    SegmentDefs.SetFileSegmentDA[@s, DAs[s.base]];
	ENDCASE;
      RETURN[FALSE];
      END;

    -- of PlugDA --
    saveDisplay ← DisplayHead↑;
    DisplayHead↑ ← 0;
    [] ← EnumerateFileSegments[FindEnds];
    [] ← EnumerateFileSegments[FindKnown];
    bufseg ← MakeDataSegment[DefaultMDSBase, 1, EasyDown];
    DAseg ← MakeDataSegment[
      DefaultMDSBase, PagesForWords[maxunknown - maxknown + 3], EasyDown];
    DAs ← DESCRIPTOR[DataSegmentAddress[DAseg] - (maxknown - 1), maxunknown + 2];
    diskrequest ← DiskDefs.DiskRequest[
      ca: DataSegmentAddress[bufseg], fixedCA: TRUE, da: @DAs[0],
      fp: @imageFile.fp, firstPage: maxknown, lastPage: maxunknown, action: ReadD,
      lastAction: ReadD, signalCheckError: FALSE,
      option: update[cleanup: BFSDefs.GetNextDA]];
    MiscDefs.SetBlock[@DAs[maxknown - 1], fillinDA, maxunknown - maxknown + 3];
    DAs[maxknown] ← maxknownDA;
    [] ← BFSDefs.ActOnPages[LOOPHOLE[@diskrequest]];
    -- we know it is an Update diskrequest
    [] ← EnumerateFileSegments[PlugDA];
    DeleteDataSegment[DAseg];
    DeleteDataSegment[bufseg];
    DisplayHead↑ ← saveDisplay;
    END;

  -- of CollectDiskAddresses --

  -- The driver


  MakeImage: PUBLIC PROCEDURE [name: STRING, merge: BOOLEAN ← TRUE] =
    BEGIN
    s: StateVector;
    InitFileRequest[];
    InitSpace[];
    s.stk[0] ← FrameOps.MyGlobalFrame[];
    s.stkptr ← 1;
    s.instbyte ← 0;
    s.dest ← FrameDefs.SwapOutCode;
    s.source ← FrameOps.GetReturnLink[];
    InstallImage[name, merge];
    RETURN WITH s;
    END;


  END..