-- MMMemory.Mesa  Edited by Sandman on February 13, 1980  3:00 PM
-- Edited by Forrest on July 15, 1980  6:13 PM

DIRECTORY
  AllocDefs USING [AllocInfo],
  AltoDefs USING [MaxVMPage, PageCount, PageNumber, PageSize],
  FrameOps USING [FlushLargeFrames],
  InlineDefs USING [BITAND],
  MMInit,
  ProcessDefs USING [DisableInterrupts, EnableInterrupts],
  SegmentDefs USING [
    DataSegmentHandle, DefaultBase, NewDataSegment, PagePointer],
  SwapperOps,
  SystemDefs USING [PagesForWords];

MMMemory: PROGRAM
  IMPORTS FrameOps, InlineDefs, ProcessDefs, SegmentDefs, SystemDefs
  EXPORTS AllocDefs, MMInit, SegmentDefs, SwapperOps, SystemDefs
  SHARES SegmentDefs =
BEGIN OPEN AltoDefs, SegmentDefs;

  Object: TYPE = MACHINE DEPENDENT RECORD [
    base: [0..--MaxVMPage--256), pages: [0..--MaxVMPage--256)];

  Handle: TYPE = POINTER TO Object;

  -- Data Segments

  DefaultBase: PageNumber = SegmentDefs.DefaultBase;

  MakeDataSegment: PUBLIC PROC [
    base: PageNumber, pages: PageCount, info: AllocDefs.AllocInfo]
    RETURNS [seg: DataSegmentHandle] =
    BEGIN
    s: Handle;
    IF pages ~IN (0..MaxVMPage+1] THEN ERROR InvalidSegmentSize[pages];
    s ← AllocateObject[];
    base ← AllocVM[base, pages ! UNWIND => LiberateObject[s]];
    s↑ ← [base: base, pages: pages];
    RETURN[LOOPHOLE[s]]
    END;

  BootDataSegment: PUBLIC PROC [base:PageNumber, pages:PageCount]
    RETURNS [seg:DataSegmentHandle] =
    BEGIN
    s: Handle;
    FOR i: PageNumber IN [base..base+pages) DO
      IF pageMap[i]#inuse THEN ERROR;
      ENDLOOP;
    s ← AllocateObject[];
    s↑ ← [base: base, pages: pages];
    RETURN[LOOPHOLE[s]]
    END;

  DeleteDataSegment: PUBLIC PROC [seg:DataSegmentHandle] =
    BEGIN
    base: PageNumber; pages: PageCount;
    s: Handle ← LOOPHOLE[seg];
    [base: base, pages: pages] ← s↑;
    UpdateVM[base, pages, free];
    LiberateObject[LOOPHOLE[seg]];
    RETURN
    END;

  DataSegmentAddress: PUBLIC PROC [seg:DataSegmentHandle] RETURNS [POINTER] =
    { RETURN[InlineDefs.BITAND[LOOPHOLE[seg, Handle]↑, 177400B]] };

  -- Memory Allocator

  PageState: TYPE = MMInit.PageState;
  pageMap: PUBLIC PACKED ARRAY [0..256) OF PageState ← ALL[inuse];
  ffvmp, lfvmp: PUBLIC AltoDefs.PageNumber;

  InvalidSegmentSize: PUBLIC SIGNAL [pages: PageCount] = CODE;
  InsufficientVM: PUBLIC SIGNAL [needed: PageCount] = CODE;
  VMnotFree: PUBLIC SIGNAL [base: PageNumber, pages: PageCount] = CODE;

  AllocVM: PROC [base: PageNumber, pages: PageCount] RETURNS [PageNumber] =
    BEGIN
    FrameOps.FlushLargeFrames[];
    IF base # DefaultBase THEN
      DO -- repeat if requested VM not free
        ProcessDefs.DisableInterrupts[];
        FOR vm: PageNumber IN [base..base+pages) DO
	  IF pageMap[vm] = inuse THEN EXIT;
	  REPEAT
	    FINISHED => GOTO found;
	  ENDLOOP;
        ProcessDefs.EnableInterrupts[];
        SIGNAL VMnotFree[base, pages];
        REPEAT
	  found => NULL
        ENDLOOP
    ELSE
      DO -- repeat if insufficient VM
        n: CARDINAL ← 0;  -- count of contiguous free pages
        ProcessDefs.DisableInterrupts[];
        base ← lfvmp;
        WHILE base IN [ffvmp..lfvmp] DO
	  IF pageMap[base] = inuse THEN n ← 0
	  ELSE IF (n ← n+1) = pages THEN GOTO foundHole;
	  base ← base-1
	  ENDLOOP;
        ProcessDefs.EnableInterrupts[];
        SIGNAL InsufficientVM[pages];
        REPEAT
	  foundHole => NULL;
        ENDLOOP;
    UpdateVM[base, pages, inuse];
    ProcessDefs.EnableInterrupts[];
    RETURN[base]
    END;


  UpdateVM: PROC [base: PageNumber, pages: PageCount, status: PageState] =
    BEGIN
    IF status = free THEN
      BEGIN
      ProcessDefs.DisableInterrupts[];
      ffvmp ← MIN[ffvmp, base];
      lfvmp ← MAX[lfvmp, base+pages-1];
      ProcessDefs.EnableInterrupts[];
      END;
    ProcessDefs.DisableInterrupts[];
    FOR base IN [base..base+pages) DO pageMap[base] ← status ENDLOOP;
    ProcessDefs.EnableInterrupts[];
    RETURN
    END;

  -- Primative Object Allocation

  MaxObjects: CARDINAL = 128;
  ObjectIndex: TYPE = CARDINAL[0..MaxObjects);

  objectTable: ARRAY ObjectIndex OF Object ← ALL[FreeObject];

  FreeObject: Object = Object[base: 0, pages: 0];

  InvalidObject: PUBLIC SIGNAL [object: POINTER] = CODE;

  AllocateObject: PROC RETURNS [Handle] =
    BEGIN
    FOR i: ObjectIndex IN ObjectIndex DO
      IF objectTable[i].pages = 0 THEN RETURN [@objectTable[i]];
      ENDLOOP;
    ERROR InsufficientVM[1];
    END;

  LiberateObject: PROC [object: Handle] = { object.pages ← 0 };

  ValidateObject: PROC [object:Handle] =
    BEGIN
    o: CARDINAL ← LOOPHOLE[object];
    base: CARDINAL ← LOOPHOLE[@objectTable];
    IF o NOT IN [base..base+SIZE[Object]*MaxObjects) THEN
      ERROR InvalidObject[object];
    RETURN
    END;

  EnumerateObjects: PROC [
    proc: PROC [Handle] RETURNS [BOOLEAN]] RETURNS [object: Handle] =
    BEGIN
    i: ObjectIndex;
    FOR i IN ObjectIndex DO
      object ← @objectTable[i];
      IF object.pages # 0 AND proc[object] THEN RETURN[object];
      ENDLOOP;
    RETURN[NIL]
    END;

  VMtoDataSegment: PUBLIC PROC [a: POINTER] RETURNS [DataSegmentHandle] =
    BEGIN
    CheckObject: PROC [h: Handle] RETURNS [BOOLEAN] =
      { RETURN[DataSegmentAddress[LOOPHOLE[h]] = PagePointer[a]] };
    RETURN[LOOPHOLE[EnumerateObjects[CheckObject]]];
    END;

  -- Simplified Data Segments

  AllocatePages, AllocateResidentPages: PUBLIC PROC [
    npages:CARDINAL] RETURNS [POINTER] =
    { RETURN[DataSegmentAddress[NewDataSegment[DefaultBase,npages]]] };

  AllocateSegment, AllocateResidentSegment: PUBLIC PROC [
    nwords:CARDINAL] RETURNS [POINTER] =
    { RETURN[AllocatePages[SystemDefs.PagesForWords[nwords]]] };

  SegmentSize: PUBLIC PROC [base:POINTER] RETURNS [CARDINAL] =
    BEGIN
    h: Handle = LOOPHOLE[VMtoDataSegment[base]];
    RETURN[IF h = NIL THEN 0 ELSE h.pages*AltoDefs.PageSize]
    END;

  FreeSegment, FreePages: PUBLIC PROC [base:POINTER] =
    BEGIN
    seg: DataSegmentHandle = VMtoDataSegment[base];
    IF seg # NIL THEN DeleteDataSegment[seg];
    RETURN
    END;


  END.....