-- MesaInit.mesa; edited by Sandman, August 27, 1980  10:01 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AllocDefs USING [SwappingProcedure, SwapStrategy, TryCodeSwapping],
  AltoDefs USING [MaxMDSPage, PageSize, PagesPerMDS],
  ControlDefs USING [GlobalFrameHandle],
  FrameDefs USING [
    EnumerateGlobalFrames, GlobalFrame, MakeCodeResident, RemoveGlobalFrame,
    UnlockCode, UnNew, ValidateGlobalFrame],
  FrameOps USING [CodeHandle],
  InlineDefs USING [BITAND, LongCOPY],
  Map USING [Clean, Entry, SETF, VacantFlags],
  MiscDefs USING [],
  Mopcodes USING [zRBL, zSTARTIO, zWBL],
  NucleusOps USING [],
  OsStaticDefs USING [OsStatics],
  ProcessDefs USING [CV, DisableInterrupts, EnableInterrupts, ParityLevel],
  Region USING [Count, Handle, HyperRegion, Index, Node, Page, PageStatus],
  SegmentDefs USING [
    AddressFromPage, AllocInfo, BankIndex, Banks, DefaultANYBase, DefaultMDSBase,
    EasyUp, EnumerateFileSegments, FileSegmentClass, FileSegmentHandle,
    LongAddressFromPage, MachineType, MakeSwappedIn, MemoryConfig, PageNumber,
    SegLockCount, SegmentHandle, SegmentLocation, SegmentType, SwapIn, Unlock],
  SwapperOps USING [
    Alloc, AllocVM, Available, BusyPage, Count, Direction, Disable, ff, FreePage,
    initMemoryConfig, lf, map, mdsIndex, mdsNodes, mdsRegion, MDSRegion,
    memConfig, Page, PageMap, PageStatus, regions, rover, Status, strategyList,
    Swapper, systemTable, Table, TableHandle, TrySwapping, Update, UpdateVM];

MesaInit: PROGRAM
  IMPORTS
    SwapperOps, FrameDefs, FrameOps, InlineDefs, Map, AllocDefs, ProcessDefs,
    Region, SegmentDefs
  EXPORTS AllocDefs, MiscDefs, NucleusOps, SwapperOps
  SHARES SegmentDefs =PUBLIC

  BEGIN OPEN ControlDefs, AllocDefs, SegmentDefs, SwapperOps;

  LastResort: SwapStrategy ← SwapStrategy[NIL, TryCodeSwapping];

  AddSwapStrategy: PUBLIC PROCEDURE [strategy: POINTER TO SwapStrategy] =
    BEGIN
    sp: POINTER TO SwapStrategy;
    ProcessDefs.DisableInterrupts[];
    FOR sp ← SwapperOps.strategyList, sp.link UNTIL sp = NIL DO
      IF sp = strategy THEN RETURN; ENDLOOP;
    strategy.link ← SwapperOps.strategyList;
    SwapperOps.strategyList ← strategy;
    ProcessDefs.EnableInterrupts[];
    RETURN
    END;

  RemoveSwapStrategy: PUBLIC PROCEDURE [strategy: POINTER TO SwapStrategy] =
    BEGIN
    sp: POINTER TO SwapStrategy;
    prev: POINTER TO SwapStrategy ← NIL;
    ProcessDefs.DisableInterrupts[];
    FOR sp ← SwapperOps.strategyList, sp.link UNTIL sp = NIL DO
      IF sp = strategy THEN
	BEGIN
	IF prev = NIL THEN SwapperOps.strategyList ← sp.link
	ELSE prev.link ← sp.link;
	EXIT
	END;
      prev ← sp;
      ENDLOOP;
    ProcessDefs.EnableInterrupts[];
    strategy.link ← NIL;
    RETURN
    END;

  MoveCode: PROCEDURE [direction: Direction] =
    BEGIN OPEN SegmentDefs;

    CheckOne: PROCEDURE [seg: FileSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      oldVMpage, newVMpage: PageNumber;
      delta: LONG CARDINAL;

      UpdateCodebase: PROCEDURE [f: GlobalFrameHandle] RETURNS [BOOLEAN] =
	BEGIN
	IF FrameOps.CodeHandle[f] = seg AND ~f.code.out THEN
	  SELECT direction FROM
	    outofMDS =>
	      BEGIN
	      f.code.longbase ← LONG[f.code.shortbase] + delta;
	      IF f.code.highHalf = 0 THEN f.code.handle ← seg;
	      END;
	    intoMDS =>
	      BEGIN
	      f.code.longbase ← f.code.longbase + delta;
	      IF f.code.highHalf # 0 THEN ERROR;
	      f.code.handle ← seg;
	      END;
	    ENDCASE;
	RETURN[FALSE]
	END;

      IF seg.class # code OR seg.lock = 0 THEN RETURN[FALSE];
      IF seg.VMpage >= 256 AND direction = outofMDS THEN RETURN[FALSE];
      IF seg.VMpage < 256 AND direction = intoMDS THEN RETURN[FALSE];
      SwapIn[seg];
      oldVMpage ← seg.VMpage;
      newVMpage ← AllocVM[
	base: IF direction = outofMDS THEN DefaultANYBase ELSE DefaultMDSBase,
	pages: seg.pages, info: [hard, bottomup, code], seg: seg];
      ProcessDefs.DisableInterrupts[];
      InlineDefs.LongCOPY[
	from: LongAddressFromPage[oldVMpage], to: LongAddressFromPage[newVMpage],
	nwords: AltoDefs.PageSize*seg.pages];
      delta ← AltoDefs.PageSize*(LONG[newVMpage] - LONG[oldVMpage]);
      [] ← FrameDefs.EnumerateGlobalFrames[UpdateCodebase];
      UpdateVM[oldVMpage, seg.pages, FreePage];
      seg.VMpage ← newVMpage;
      UpdateVM[newVMpage, seg.pages, seg];
      ProcessDefs.EnableInterrupts[];
      Unlock[seg];
      RETURN[FALSE]
      END;

    IF ~memConfig.useXM OR memConfig.banks = 100000B THEN RETURN;
    [] ← EnumerateFileSegments[CheckOne];
    END;

  EnableHyperspace: PROCEDURE = {
    IF memConfig.useXM THEN
      FOR i: BankIndex IN (FIRST[BankIndex]..LAST[BankIndex]] DO
	IF LOOPHOLE[memConfig.banks, Banks][i] THEN EnableBank[i]; ENDLOOP};

  DisableHyperspace: PROCEDURE =
    BEGIN
    i: BankIndex;
    FOR i IN (FIRST[BankIndex]..LAST[BankIndex]] DO DisableBank[i]; ENDLOOP;
    RETURN
    END;

  NullManager: PROGRAM [Region.Index] RETURNS [Region.Handle] = LOOPHOLE[0];
  originalManager: PROGRAM [Region.Index] RETURNS [Region.Handle] ← NullManager;
  nHyperRegions: CARDINAL ← 0;

  EnableBank: PROCEDURE [bank: BankIndex] =
    BEGIN
    bankManager: PROGRAM [Region.Index] RETURNS [Region.Handle];
    IF bank = 0 THEN RETURN;
    IF originalManager = NullManager THEN
      bankManager ← originalManager ← Region.HyperRegion
    ELSE bankManager ← NEW Region.HyperRegion;
    IF nHyperRegions = 0 THEN
      FrameDefs.MakeCodeResident[FrameDefs.GlobalFrame[bankManager]];
    SwapperOps.regions[bank] ← START bankManager[bank];
    nHyperRegions ← nHyperRegions + 1;
    RETURN
    END;

  DisableBank: PROCEDURE [bank: BankIndex] =
    BEGIN OPEN FrameDefs;
    bankManager: PROGRAM [Region.Index] RETURNS [Region.Handle];
    region: Region.Handle;
    IF bank = 0 THEN RETURN;
    IF (region ← SwapperOps.regions[bank]) = NIL THEN RETURN;
    bankManager ← LOOPHOLE[GlobalFrame[region.alloc]];
    region.disable[FALSE];
    SwapperOps.regions[bank] ← NIL;
    IF bankManager # originalManager THEN UnNew[LOOPHOLE[bankManager]];
    IF (nHyperRegions ← nHyperRegions - 1) = 0 THEN UnlockCode[originalManager];
    RETURN
    END;

  -- D0/Dorado hyperspace stuff


  ComputeRealMemory: PUBLIC PROC RETURNS [pages: CARDINAL, banks: Banks] =
    BEGIN OPEN AltoDefs, SwapperOps;
    banks ← ALL[FALSE];
    pages ← 0;
    SELECT memConfig.AltoType FROM
      AltoIIXM =>
	IF memConfig.xmMicroCode THEN {
	  word: CARDINAL;
	  parity: POINTER = ProcessDefs.CV[ProcessDefs.ParityLevel];
	  ProcessDefs.CV[ProcessDefs.ParityLevel] ← NIL;
	  FOR i: CARDINAL IN [1..3] DO
	    word ← RBL[LP[0, i]];
	    WBL[word + 1, LP[0, i]];
	    IF RBL[LP[0, i]] = word + 1 THEN {
	      WBL[word, LP[0, i]]; banks[i] ← TRUE; pages ← pages + PagesPerMDS};
	    ENDLOOP;
	  ProcessDefs.CV[ProcessDefs.ParityLevel] ← parity}
	ELSE {pages ← PagesPerMDS; banks[0] ← TRUE};
      D0, Dorado => {
	t: Map.Entry;
	FOR pages ← PagesPerMDS, pages + 1 DO
	  t ← Map.SETF[pages, Map.Clean];
	  [] ← Map.SETF[pages, t];
	  IF t.flags = Map.VacantFlags THEN EXIT;
	  ENDLOOP;
	FOR i: CARDINAL IN [0..(pages + MaxMDSPage)/PagesPerMDS) DO
	  banks[i] ← TRUE; ENDLOOP};
      ENDCASE => {pages ← PagesPerMDS; banks[0] ← TRUE};
    RETURN
    END;

  RBL: PROCEDURE [LONG POINTER] RETURNS [CARDINAL] = MACHINE CODE
    BEGIN Mopcodes.zRBL, 0 END;

  WBL: PROCEDURE [CARDINAL, LONG POINTER] = MACHINE CODE
    BEGIN Mopcodes.zWBL, 0 END;

  LP: PROCEDURE [CARDINAL, CARDINAL] RETURNS [LONG POINTER] = MACHINE CODE
    BEGIN END;

  InitMemoryConfig: PROCEDURE =
    BEGIN
    pages: CARDINAL;
    banks: Banks;
    MDSOnly: CARDINAL = 100000B;
    memConfig ← initMemoryConfig↑;
    [pages: pages, banks: banks] ← ComputeRealMemory[];
    memConfig.banks ← InlineDefs.BITAND[banks, memConfig.banks];
    IF memConfig.banks # MDSOnly THEN memConfig.useXM ← memConfig.xmMicroCode;
    END;

  Disable: PROCEDURE [BOOLEAN] = BEGIN END;

  InitMemory: PROCEDURE [ffvmp, lfvmp: CARDINAL] =
    BEGIN OPEN SwapperOps;
    page: [0..AltoDefs.MaxMDSPage];
    pages: CARDINAL ← lfvmp - ffvmp + 1;
    node: Region.Node = AddressFromPage[ffvmp];
    START Swapper;
    START MDSRegion;
    mdsRegion ←
      [basePage: 0, alloc: Alloc, update: Update, available: Available,
	status: Status, disable: Disable, swap: TrySwapping, hole: pages];
    ff ← rover ← ffvmp;
    lf ← lfvmp;
    node↑ ← [base: ffvmp, pages: pages, fwd: @mdsNodes, back: @mdsNodes];
    mdsNodes ← [base: 177777B, pages: 0, fwd: node, back: node];
    FOR page IN [0..AltoDefs.MaxMDSPage] DO
      map[page] ← IF page IN [ffvmp..lfvmp] THEN FreePage ELSE BusyPage; ENDLOOP;
    regions ← ALL[NIL];
    regions[mdsIndex ← 0] ← @mdsRegion;
    strategyList ← @LastResort;
    InitMemoryConfig[];
    systemTable ←
      [mdsMap: map, memConfig: @memConfig, table: NIL, regions: @regions];
    END;

  -- Fake Modules


  DestroyFakeModule: PROCEDURE [f: GlobalFrameHandle]
    RETURNS [seg: SegmentDefs.FileSegmentHandle, offset: CARDINAL] =
    BEGIN
    FrameDefs.ValidateGlobalFrame[f];
    seg ← FrameOps.CodeHandle[f];
    IF seg = NIL THEN RETURN[NIL, 0];
    IF ~f.shared THEN seg.class ← other;
    FrameDefs.RemoveGlobalFrame[f];
    ProcessDefs.DisableInterrupts[];
    IF f.code.out THEN BEGIN f.code.out ← FALSE; offset ← f.code.offset; END
    ELSE offset ← f.code.offset - seg.VMpage*AltoDefs.PageSize;
    ProcessDefs.EnableInterrupts[];
    RETURN
    END;

  -- Get Network Number

  wordsPerPup: INTEGER = 280;
  Byte: TYPE = [0..255];

  PupHeader: TYPE = MACHINE DEPENDENT RECORD [
    eDest, eSource: Byte,
    eWord2, pupLength: INTEGER,
    transportControl, pupType: Byte,
    pupID1, pupID2: INTEGER,
    destNet, destHost: Byte,
    destSocket1, destSocket2: INTEGER,
    sourceNet, sourceHost: Byte,
    sourceSocket1, sourceSocket2: INTEGER,
    xSum: CARDINAL];

  Pup: TYPE = MACHINE DEPENDENT RECORD [
    head: PupHeader, junk: ARRAY [0..100] OF WORD];

  EthernetDeviceBlock: TYPE = MACHINE DEPENDENT RECORD [
    EPLocMicrocodeStatus, EPLocHardwareStatus: Byte,
    EBLocInterruptBit: WORD,
    EELocInputFinishCount: INTEGER,
    ELLocCollisionMagic: WORD,
    EILocInputCount: INTEGER,
    EILocInputPointer: POINTER,
    EOLocOutputCount: INTEGER,
    EOLocOutputPointer: POINTER];

  timer: POINTER TO INTEGER = LOOPHOLE[430B];

  GetNetworkNumber: PROCEDURE RETURNS [CARDINAL] =
    BEGIN
    myHost: Byte ← OsStaticDefs.OsStatics.SerialNumber;
    then: INTEGER;
    now: INTEGER;
    device: POINTER TO EthernetDeviceBlock ← LOOPHOLE[600B];
    xpup: Pup;
    pup: POINTER TO Pup = @xpup;
    -- StartIO is Mesa bytecode used to control Ethernet interface
    StartIO: PROCEDURE [WORD] = MACHINE CODE BEGIN Mopcodes.zSTARTIO END;

    outputCommand: WORD = 1;
    inputCommand: WORD = 2;
    resetCommand: WORD = 3;
    gatewayRequest: PupHeader ←
      [eDest: 0, eSource: myHost, eWord2: 1000B, pupLength: 22,
	transportControl: 0, pupType: 200B, pupID1:, pupID2:, destNet: 0,
	destHost: 0, destSocket1: 0, destSocket2: 2, sourceNet: 0,
	sourceHost: myHost, sourceSocket1: 0, sourceSocket2: 2, xSum: 177777B];
    device.EBLocInterruptBit ← 0;
    THROUGH [0..2) DO
      StartIO[resetCommand];
      device↑ ← EthernetDeviceBlock[
	EPLocMicrocodeStatus: 0, EPLocHardwareStatus: 0, EBLocInterruptBit: 0,
	EELocInputFinishCount: 0, ELLocCollisionMagic: 0, EILocInputCount: 0,
	EILocInputPointer: pup, EOLocOutputCount: 13,
	EOLocOutputPointer: @gatewayRequest];
      StartIO[outputCommand];
      then ← timer↑;
      DO
	IF device.EPLocHardwareStatus # 0 THEN
	  BEGIN
	  IF device.EPLocMicrocodeStatus = 0 AND pup.head.eWord2 = 1000B AND
	    wordsPerPup + 2 - device.EELocInputFinishCount > 13 AND
	    pup.head.destSocket1 = 0 AND pup.head.destSocket2 = 2 AND
	    pup.head.pupType = 201B THEN RETURN[pup.head.sourceNet];
	  device↑ ← EthernetDeviceBlock[
	    EPLocMicrocodeStatus: 0, EPLocHardwareStatus: 0, EBLocInterruptBit: 0,
	    EELocInputFinishCount: 0, ELLocCollisionMagic: 0,
	    EILocInputCount: wordsPerPup + 2, EILocInputPointer: pup,
	    EOLocOutputCount: 0, EOLocOutputPointer: NIL];
	  StartIO[inputCommand];
	  END;
	now ← timer↑;
	IF now - then > 14 THEN EXIT;
	ENDLOOP;
      ENDLOOP;
    RETURN[0];
    END;


  END...