-- File: Bootmesa.Mesa
-- Last edited by Sandman; September 12, 1980  2:37 PM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoDefs USING [Address, PageCount, PageNumber, PageSize],
  AltoFileDefs USING [eofDA, NullFP],
  BcdDefs USING [Base, FTSelf, MTIndex, VersionStamp],
  BcdOps USING [BcdBase, MTHandle, ProcessModules],
  CacheOps USING [Flush, Init, PageNumber, READ, WRITE],
  BootmesaOps,
  CommanderDefs USING [AddCommand, CommandBlockHandle],
  ControlDefs USING [
    AV, ControlLink, FrameCodeBase, FrameVec, GFT, GFTIndex, GFTItem,
    GlobalFrameHandle, MainBodyIndex, LargeReturnSlot, LastAVSlot, NullFrame,
    NullGlobalFrame, SpecialReturnSlot, StateVector],
  InlineDefs USING [COPY],
  IODefs USING [WriteLine, WriteString],
  LoadStateFormat USING [LoadState],
  MiscDefs USING [GetNetworkNumber],
  OsStaticDefs USING [OsStatics],
  ProcessDefs USING [Priority],
  ProcessOps USING [FirstProcess, FirstStateVector, LastProcess],
  PSBDefs USING [PSB],
  SDDefs USING [SD, sGFTLength, sXferTrap],
  SegmentDefs USING [
    AddressFromPage, CloseFile, DataSegmentHandle, DeleteFileSegment, EasyUp,
    FileHandle, FileSegmentAddress, FileSegmentHandle, MapFileSegment,
    NewFileSegment, OpenFile, PageFromAddress, Read, SwapIn, SwapUp,
    Unlock, Write],
  SegOps USING [
    CodeClass, DefaultFile, InitSegMachinery, NewSeg, Seg, SwapInSeg, vmFile],
  String USING [AppendString],
  Time USING [Current];

Bootmesa: PROGRAM
  IMPORTS
    CacheOps, BootmesaOps, CommanderDefs, InlineDefs, IODefs, MiscDefs,
    SegmentDefs, String, Time, SegOps, BcdOps
  EXPORTS BootmesaOps
  SHARES ControlDefs, ProcessDefs =
  BEGIN OPEN AltoDefs, ControlDefs, BootmesaOps, SegOps;
  
  data: POINTER TO BootmesaOps.BootData ← @dataObject;
  
  FileHandle: TYPE = SegmentDefs.FileHandle;
  FileSegmentHandle: TYPE = SegmentDefs.FileSegmentHandle;
  DataSegmentHandle: TYPE = SegmentDefs.DataSegmentHandle;
  
  -- utility procedures
  
  BootAbort: PUBLIC SIGNAL = CODE;
  
  BootmesaError: PUBLIC PROCEDURE [msg: STRING] =
    BEGIN OPEN IODefs;
    WriteString["Bootmesa Error: "L];
    WriteLine[msg];
    SIGNAL BootAbort;
    END;
    
  -- Frame Allocation
  
  paramsperframe: CARDINAL = 1;
  framesloaded: CARDINAL ← 0;
  framebase: POINTER;
  FrameStackFull: ERROR = CODE;
  extraFramesDesired: BOOLEAN ← FALSE;
  
  frameweight: PACKED ARRAY [0..LastAVSlot] OF CARDINAL ←
    [9, 15, 10, 8, 7, 6, 5, 7, 3, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
      0, 0, 0, 0, 0];
  extraframes: ARRAY [0..LastAVSlot] OF CARDINAL;
  
  AllocGlobalFrame: PUBLIC PROCEDURE [
    framesize, nlinks: CARDINAL, framelinks: BOOLEAN] RETURNS [frame: POINTER] =
    BEGIN
    framebase ← framebase - framesize;
    frame ← framebase ← framebase - LOOPHOLE[framebase, CARDINAL] MOD 4;
    IF framelinks THEN framebase ← framebase - nlinks;
    IF LOOPHOLE[framebase, CARDINAL] < CARDINAL[FirstVMPage*PageSize] THEN
      ERROR FrameStackFull;
    RETURN
    END;
    
  AllocFrame: PROCEDURE [findex: CARDINAL] RETURNS [frame: POINTER] =
    BEGIN
    framesloaded ← framesloaded + 1;
    frame ← framebase ← framebase - (FrameVec[findex] + 1);
    CacheOps.WRITE[frame - 1, findex];
    RETURN
    END;
    
  AddFrame: PROCEDURE [findex: CARDINAL] =
    BEGIN OPEN CacheOps;
    p: POINTER = AllocFrame[findex];
    WRITE[p, READ[AVbase + findex]];
    WRITE[AVbase + findex, p];
    END;
    
  AllocateExtraFrames: PROCEDURE =
    BEGIN
    i, j: CARDINAL;
    FOR i IN [0..LENGTH[extraframes]) DO
      FOR j IN [0..extraframes[i]) DO AddFrame[i]; ENDLOOP; ENDLOOP;
    END;
    
  DivideAllocationArea: PROCEDURE [base: Address, size: CARDINAL] =
    BEGIN OPEN CacheOps;
    p: Address ← base + 4; -- allow initial dead space
    i, j, s, c, sum: CARDINAL;
    fw: ARRAY [0..LENGTH[frameweight]) OF CARDINAL;
    FOR i IN [0..LENGTH[fw]) DO fw[i] ← frameweight[i] ENDLOOP;
    size ← size - 4;
    sum ← 0;
    FOR i IN [0..LENGTH[fw]) DO sum ← sum + FrameVec[i]*fw[i] ENDLOOP;
    c ← MAX[size/sum, 1];
    WHILE size > FrameVec[0] DO
      FOR i IN [0..LENGTH[FrameVec]) DO
	s ← FrameVec[i];
	IF s MOD 4 = 3 THEN s ← s + 1;
	FOR j IN [1..fw[i]*c] DO
	  -- add to alloc vector
	  IF size < s THEN EXIT;
	  WRITE[p - 1, i]; -- hidden link word
	  WRITE[p, READ[AVbase + i]];
	  WRITE[AVbase + i, p];
	  p ← p + s;
	  size ← size - s;
	  ENDLOOP;
	ENDLOOP;
      FOR i IN [0..LENGTH[fw] - 1) DO fw[i] ← MAX[fw[i]/3, 1] ENDLOOP;
      c ← 1;
      ENDLOOP;
    RETURN
    END;
    
  SetResidentFramePages: PUBLIC PROCEDURE [n: CARDINAL] =
    BEGIN IF residentFramePages = 0 THEN residentFramePages ← n END;
    
  ResidentFramePages: PROCEDURE [n: CARDINAL] = BEGIN residentFramePages ← n END;
    
  residentFramePages: CARDINAL ← 0;
  FramePages: PageCount;
  
  -- initialization
  
  AVbase, GFTbase, SDbase, SVbase: POINTER ← NIL;
  
  DefaultFirstVMPage: PageNumber ← 2;
  DefaultLastVMPage: PageNumber ← 370B;
  FirstVMPage: PageNumber ← 0;
  LastVMPage: PageNumber ← 0;
  
  GetMemoryLimits: PUBLIC PROCEDURE RETURNS [fp, lp: PageNumber] =
    BEGIN RETURN[FirstVMPage, LastVMPage] END;
    
  SetMemoryLimits: PROCEDURE [fp, lp: PageNumber] =
    BEGIN FirstVMPage ← fp; LastVMPage ← lp; END;
    
  SetDefaultMemoryLimits: PUBLIC PROCEDURE [fp, lp: PageNumber] =
    BEGIN DefaultFirstVMPage ← fp; DefaultLastVMPage ← lp; END;
    
  StateVectors: CARDINAL =
    (LAST[ProcessDefs.Priority] + 1)*SIZE[ControlDefs.StateVector];
  
  DefaultNProcesses: PUBLIC CARDINAL ←
    (AltoDefs.PageSize - StateVectors)/SIZE[PSBDefs.PSB];
  nProcesses: CARDINAL ← 0;
  
  SetNumberProcesses: PROCEDURE [n: CARDINAL] = BEGIN nProcesses ← n; END;
    
  SetDefaultNProcesses: PUBLIC PROCEDURE [n: CARDINAL] =
    BEGIN DefaultNProcesses ← n; END;
    
  DefaultGFTLength: CARDINAL ← 256;
  GFTLength: CARDINAL ← 0;
  
  SetGFTLength: PROCEDURE [l: CARDINAL] = BEGIN GFTLength ← l; END;
    
  SetDefaultGFTLength: PUBLIC PROCEDURE [l: CARDINAL] =
    BEGIN DefaultGFTLength ← l; END;
    
  AssignDefaults: PROCEDURE =
    BEGIN
    IF FirstVMPage = 0 THEN FirstVMPage ← DefaultFirstVMPage;
    IF LastVMPage = 0 THEN LastVMPage ← DefaultLastVMPage;
    IF GFTLength = 0 THEN GFTLength ← DefaultGFTLength;
    IF nProcesses = 0 THEN nProcesses ← DefaultNProcesses;
    AVbase ← ControlDefs.AV;
    SDbase ← SDDefs.SD;
    GFTbase ← ControlDefs.GFT;
    RETURN
    END;
    
  InitializeVM: PROCEDURE [framep: PageCount] =
    BEGIN OPEN SegmentDefs, CacheOps, SegOps;
    s: Seg;
    i, GFTpages, SVpages: CARDINAL;
    fp: PageNumber;
    AssignDefaults[];
    fp ← FirstVMPage;
    FramePages ← framep;
    InitSegMachinery[FirstVMPage, LastVMPage];
    CacheOps.Init["BootMesa.Scratch", FirstVMPage, LastVMPage];
    framebase ← LOOPHOLE[(LastVMPage + 1)*PageSize];
    -- av and sd
    s ← NewSeg[DefaultFile, PageFromAddress[AVbase], 1, TRUE];
    SegOps.vmFile ← s.realFile;
    FOR i IN [0..PageSize) DO WRITE[AVbase + i, 0] ENDLOOP;
    FOR i IN [0..18) DO WRITE[AVbase + i, 4*(i + 1) + 2] ENDLOOP;
    WRITE[AVbase + 11, 1];
    FOR i IN [18..LastAVSlot] DO WRITE[AVbase + i, 1] ENDLOOP;
    WRITE[AVbase + LargeReturnSlot, 1];
    WRITE[AVbase + SpecialReturnSlot, 1];
    --gft
    GFTpages ← (GFTLength*SIZE[GFTItem] + (PageSize - 1))/PageSize;
    s ← NewSeg[DefaultFile, PageFromAddress[GFTbase], GFTpages, TRUE];
    FOR i IN [0..GFTLength*SIZE[GFTItem]) DO WRITE[GFTbase + i, 0] ENDLOOP;
    InitializeGFT[GFTbase, GFTLength];
    WRITE[SDbase + SDDefs.sGFTLength, GFTLength];
    -- process storage initialization
    SVpages ←
      (nProcesses*SIZE[PSBDefs.PSB] + StateVectors + (PageSize - 1))/PageSize;
    s ← NewSeg[DefaultFile, PageFromAddress[GFTbase] + GFTpages, SVpages, TRUE];
    SVbase ← SegmentDefs.AddressFromPage[s.vmPage];
    WRITE[ProcessOps.FirstStateVector, SVbase];
    WRITE[ProcessOps.FirstProcess, SVbase + StateVectors];
    WRITE[
      ProcessOps.LastProcess,
      SVbase + StateVectors + (nProcesses - 1)*SIZE[PSBDefs.PSB]];
    FOR i IN [0..SVpages*AltoDefs.PageSize) DO WRITE[SVbase + i, 0] ENDLOOP;
    RETURN
    END;
    
  InitializeHeap: PUBLIC PROCEDURE =
    BEGIN
    stackbase: PageNumber;
    stackpages: PageCount;
    frameseg: Seg;
    frames: POINTER;
    stackbase ← LOOPHOLE[framebase, CARDINAL]/PageSize;
    stackpages ← LastVMPage - stackbase + 1;
    frameseg ← NewSeg[
      DefaultFile, stackbase - FramePages, FramePages + stackpages, TRUE];
    frames ← SegmentDefs.AddressFromPage[frameseg.vmPage];
    DivideAllocationArea[LOOPHOLE[frames], framebase - frames];
    RETURN
    END;
    
  imageStamp: BcdDefs.VersionStamp;
  
  InitializeBootmesa: PUBLIC PROCEDURE [root: STRING]
    RETURNS [BcdDefs.VersionStamp] =
    BEGIN
    SetResidentFramePages[10];
    InitializeVM[residentFramePages];
    InitializeBootScript[];
    data.imageFileRoot.length ← 0;
    String.AppendString[data.imageFileRoot, root];
    BootmesaOps.OpenLoadmap[root];
    imageStamp ←
      [net: MiscDefs.GetNetworkNumber[],
	host: OsStaticDefs.OsStatics.SerialNumber,
	time: LOOPHOLE[Time.Current[]]];
    RETURN[imageStamp]
    END;
    
  AdjustBcd: PUBLIC PROCEDURE [fs: Seg] =
    BEGIN OPEN SegmentDefs;
    sgb: BcdDefs.Base;
    bcd: BcdOps.BcdBase;
    bseg: FileSegmentHandle ← fs.link2;
    AdjustCodeSegment: PROCEDURE [mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
      RETURNS [BOOLEAN] =
      BEGIN
      frame: GlobalFrameHandle ← data.moduleTable[mth.gfi].frame;
      sgb[mth.code.sgi].file ← BcdDefs.FTSelf;
      sgb[mth.code.sgi].base ← data.moduleTable[mth.gfi].code.imageBase;
      RETURN[FALSE];
      END;
    bcd ← FileSegmentAddress[bseg];
    sgb ← LOOPHOLE[bcd + bcd.sgOffset];
    [] ← BcdOps.ProcessModules[bcd, AdjustCodeSegment];
    MapFileSegment[bseg, fs.realFile, fs.imageBase];
    RETURN
    END;
    
  InitializeLoadState: PUBLIC PROCEDURE =
    BEGIN OPEN SegmentDefs;
    gft: POINTER TO ARRAY [0..0) OF GFTItem = GFTbase;
    ls: Seg = lsseg;
    initls: Seg = initlsseg;
    bcdseg: Seg = fakebcdseg;
    iseg: FileSegmentHandle ← NewFileSegment[
      initls.realFile, initls.imageBase, initls.pages, Read + Write];
    seg: FileSegmentHandle ← NewFileSegment[
      ls.realFile, ls.imageBase, ls.pages, Read + Write];
    bseg: FileSegmentHandle ← bcdseg.link2;
    loadstate: LoadStateFormat.LoadState;
    SwapIn[seg];
    loadstate ← FileSegmentAddress[seg];
    loadstate.bcds[0] ←
      [exports: data.bcd.nExports # 0, typeExported: data.bcd.typeExported,
	pages: bcdseg.pages,
	body: alto[
	da: AltoFileDefs.eofDA, base: bcdseg.imageBase, fp: AltoFileDefs.NullFP]];
    SwapIn[iseg];
    InlineDefs.COPY[
      to: FileSegmentAddress[iseg], from: loadstate,
      nwords: AltoDefs.PageSize*seg.pages];
    Unlock[iseg];
    DeleteFileSegment[iseg];
    Unlock[seg];
    DeleteFileSegment[seg];
    data.image.prefix.loadStateBase ← ls.imageBase;
    data.image.prefix.initialLoadStateBase ← initls.imageBase;
    data.image.prefix.loadStatePages ← ls.pages;
    data.header.loadState ← ls.index;
    data.header.initLoadState ← initls.index;
    data.header.bcd ← bcdseg.index;
    END;
    
  MapCode: PROCEDURE [class: SegOps.CodeClass] =
    BEGIN
    i: CARDINAL;
    fs: Seg;
    frame: GlobalFrameHandle;
    mt: DESCRIPTOR FOR ARRAY OF ModuleInfo ← data.moduleTable;
    mth: BcdOps.MTHandle;
    FOR i IN [1..LENGTH[mt]) DO
      IF mt[i].class # class OR mt[i].whenLoaded = notLoaded THEN LOOP;
      [frame: frame, code: fs, mth: mth] ← mt[i];
      IF mth.gfi # i THEN LOOP;
      IF ~fs.in THEN SwapInSeg[fs];
      fs.resident ← class = resident;
      ENDLOOP;
    RETURN
    END;
    
  MapResidentCode: PUBLIC PROCEDURE = BEGIN MapCode[resident]; RETURN END;
    
  MapSwappedInCode: PUBLIC PROCEDURE = BEGIN MapCode[in]; RETURN END;
    
  swapInBcd: BOOLEAN ← TRUE;
  
  NoSwapBcd: PROCEDURE = BEGIN swapInBcd ← FALSE END;
    
  MapLoadStates: PUBLIC PROCEDURE =
    BEGIN
    SwapInSeg[initlsseg];
    SwapInSeg[lsseg];
    IF swapInBcd THEN SwapInSeg[fakebcdseg];
    RETURN
    END;
    
  TurnOffStartTrap: PUBLIC PROCEDURE =
    BEGIN
    Mesa: PROCEDURE [gfi: ControlDefs.GFTIndex] RETURNS [BOOLEAN] =
      BEGIN
      f: ControlDefs.GlobalFrameHandle;
      fs: Seg;
      mth: BcdOps.MTHandle;
      fcb: ControlDefs.FrameCodeBase;
      [frame: f, code: fs, mth: mth] ← data.moduleTable[gfi];
      fcb.shortbase ← SegmentDefs.AddressFromPage[fs.vmPage] + mth.code.offset;
      fcb.out ← FALSE;
      CacheOps.WRITE[@f.code.shortbase, fcb.shortbase];
      RETURN[FALSE];
      END;
    [] ← EnumerateNoTrapModules[Mesa];
    RETURN
    END;
    
  XFER: PUBLIC PROCEDURE [dest: GlobalFrameHandle] =
    BEGIN
    data.image.prefix.state.stk[0] ← FinishBootScript[
      ControlDefs.NullGlobalFrame];
    data.image.prefix.state.stkptr ← 1;
    data.image.prefix.state.dest ← SetupMainBodyProc[dest];
    data.image.prefix.state.source ← ControlDefs.NullFrame;
    data.image.prefix.version ← LOOPHOLE[imageStamp];
    BootmesaOps.CloseLoadmap[];
    [] ← CacheOps.Flush[0, SegmentDefs.EasyUp, NIL];
    SegmentDefs.SwapUp[data.headerSeg];
    CleanupImageFile[];
    RETURN
    END;
    
  SetupMainBodyProc: PROCEDURE [dest: GlobalFrameHandle]
    RETURNS [proc: ControlLink] =
    BEGIN OPEN CacheOps;
    fw: FirstFrameWord ← READ[dest];
    proc ← [procedure[gfi: fw.gfi, ep: MainBodyIndex, tag: procedure]];
    WRITE[SDbase + SDDefs.sXferTrap, proc];
    fw.started ← TRUE;
    WRITE[dest, fw];
    RETURN
    END;
    
  CleanupImageFile: PROCEDURE =
    BEGIN OPEN SegmentDefs;
    file: FileHandle = data.imageFile;
    swapCount: CARDINAL = file.swapcount;
    file.swapcount ← 0;
    CloseFile[file];
    OpenFile[file];
    file.swapcount ← swapCount;
    RETURN
    END;
    
  BEGIN OPEN CommanderDefs;
  command: CommandBlockHandle;
  command ← AddCommand["GFTLength", LOOPHOLE[SetGFTLength], 1];
  command.params[0] ← [type: numeric, prompt: "GFTLength"];
  command ← AddCommand["NumberProcesses", LOOPHOLE[SetNumberProcesses], 1];
  command.params[0] ← [type: numeric, prompt: "NumberProcesses"];
  command ← AddCommand["ResidentFramePages", LOOPHOLE[ResidentFramePages], 1];
  command.params[0] ← [type: numeric, prompt: "ResidentFramePages"];
  command ← AddCommand["SetMemoryBounds", LOOPHOLE[SetMemoryLimits], 2];
  command.params[0] ← [type: numeric, prompt: "FirstVMPage"];
  command.params[1] ← [type: numeric, prompt: "LastVMPage"];
  [] ← AddCommand["NoSwapBcd", LOOPHOLE[NoSwapBcd], 0];
  
  END;
  
  END..