-- File: VMStorageMgr.mesa
-- Last edited by
--  HGM: 13-Nov-84  2:58:23, Fix Warnings from poolPages
--  Gobbel: 30-Sep-81 13:11:50
--  M. Johnson: 11-Jan-82 14:15:32
--  Hankins:	25-Jul-84 12:20:04	Klamath update (space rework)

DIRECTORY
  Environment USING [PageNumber],
  File USING [nullFile],
  Heap USING [CreateMDS, DeleteMDS],
  Space USING [
    Allocate, Deallocate, Interval, LongPointerFromPage, MapAt, MDS, PageCount,
    PageFromLongPointer, PageOffset, Pointer, UnmapAt, wordsPerPage],
  VMStorage USING [];

VMStorageMgr: MONITOR
  IMPORTS Heap, Space  --VMSpecial--
  EXPORTS VMStorage =

  BEGIN


  -- Types and Related Constants --

  BitIndex: TYPE = CARDINAL [0..4096);
  -- (restriction of current implementation)

  AllocArray: TYPE = PACKED ARRAY [0..poolPages) OF BOOLEAN;

  Page: TYPE = ARRAY [0..Space.wordsPerPage) OF WORD;


  -- Constants and Global Variables --

  inUse: BOOLEAN = TRUE;
  free: BOOLEAN = FALSE;

  poolPages: CARDINAL = 50;
  freePageCount: Space.PageCount ← poolPages;
  allocMap: AllocArray ← ALL[free];
  rover: BitIndex ← 0;

  longTermSize: Space.PageCount = 2;
  shortTermSize: Space.PageCount = 2;

  poolSpace: Space.Interval;
  storagePool: POINTER TO ARRAY [0..poolPages) OF Page;

  -- Miscellaneous Declarations --

  AllocMapBug: ERROR = CODE;
  NoMemory: ERROR = CODE;
  PageAlreadyFree: ERROR = CODE;

  -- Procedures and Variables Exported to VMStorage --

  -- Node-Level Allocator --

  longTerm, shortTerm: PUBLIC MDSZone;

  -- Page-Level Allocator --

  AllocatePage: PUBLIC ENTRY PROCEDURE RETURNS [POINTER] =
    -- allocates a single page of main memory, returning its address.
    BEGIN
    Inc: PROCEDURE [bit: BitIndex] RETURNS [BitIndex] = INLINE {
      RETURN[(bit + 1) MOD poolPages]};
    IF freePageCount = 0 THEN ERROR NoMemory;
    FOR i: BitIndex ← Inc[rover], Inc[i] UNTIL i = rover DO
      IF allocMap[i] = free THEN {allocMap[i] ← inUse; rover ← i; EXIT};
      REPEAT FINISHED => ERROR AllocMapBug;
      ENDLOOP;
    freePageCount ← freePageCount - 1;
    RETURN[@storagePool[rover]];
    END;

  FreePage: PUBLIC ENTRY PROCEDURE [p: POINTER] =
    -- releases the single page beginning at 'p'.
    BEGIN
    bit: BitIndex = LOOPHOLE[p - storagePool];
    IF allocMap[bit] # inUse THEN ERROR PageAlreadyFree;
    allocMap[bit] ← free;
    freePageCount ← freePageCount + 1;
    END;

  InitializeStorage: PUBLIC PROCEDURE =
    -- initializes the main memory allocator.
    BEGIN InitializePageLevel[]; InitializeNodeLevel[]; END;

  FinalizeStorage: PUBLIC PROCEDURE =
    -- finalizes the main memory allocator.
    BEGIN FinalizeNodeLevel[]; FinalizePageLevel[]; END;


  -- Internal Procedures --

  -- Node-Level Allocator --

  InitializeNodeLevel: PROCEDURE =
    BEGIN
    longTerm ← Heap.CreateMDS[initial: longTermSize];
    shortTerm ← Heap.CreateMDS[initial: shortTermSize];
    END;

  FinalizeNodeLevel: PROCEDURE =
    BEGIN Heap.DeleteMDS[longTerm]; Heap.DeleteMDS[shortTerm]; END;


  -- Page-Level Allocator --

  InitializePageLevel: PROCEDURE =
    BEGIN
    firstPage: Environment.PageNumber;
    poolSpace ← Space.Allocate[count: poolPages, within: Space.MDS[]];
    storagePool ← Space.Pointer[poolSpace.pointer];
    firstPage ← Space.PageFromLongPointer[poolSpace.pointer];
    FOR p: Space.PageOffset IN [0..poolPages) DO
      [] ← Space.MapAt[
        at: [pointer: Space.LongPointerFromPage[firstPage + p], count: 1],
        window: [file: File.nullFile, base: 0, count: 1], class: data];
      ENDLOOP;
    END;

  FinalizePageLevel: PROCEDURE =
    BEGIN
    firstPage: Environment.PageNumber ← Space.PageFromLongPointer[
      poolSpace.pointer];
    FOR p: Space.PageOffset IN [0..poolPages) DO
      [] ← Space.UnmapAt[pointer: Space.LongPointerFromPage[firstPage + p]];
      ENDLOOP;
    Space.Deallocate[poolSpace];
    END;

  END.