-- File: XDMemCache.Mesa
-- Last edited by
--                Bruce; August 27, 1980  1:41 PM
--                Sandman; July 21, 1980  10:39 AM
--                Johnsson; July 11, 1980  11:11 AM

DIRECTORY
  BFSDefs USING [ActOnPages, GetNextDA],
  DebugOps USING [Lengthen, LongREAD, ShortREAD],
  DebugUsefulDefs USING [],
  DiskDefs USING [DiskRequest],
  Drum USING [],
  Init USING [],
  Inline USING [COPY, HighHalf, LongDivMod, LongMult, LongNumber],
  MachineDefs USING [eofDA, fillinDA, PageSize, vDA],
  Mopcodes USING [zPOP, zR0, zRBL, zW0, zWBL],
  SegmentDefs USING [
    BankIndex, DefaultBase, DeleteFileSegment, FileHandle, FileSegmentAddress,
    FileSegmentHandle, HardUp, LockFile, MakeSwappedIn, NewFileSegment, Read,
    SetFileSegmentDA, Unlock],
  State USING [GetGS, GSHandle],
  Storage USING [Free, FreePages, Node, Pages],
  SwapperOps USING [RegionTable, SystemTable];

XDMemCache: PROGRAM
  IMPORTS BFSDefs, DebugOps, Inline, State, Storage, SegmentDefs
  EXPORTS DebugOps, DebugUsefulDefs, Drum, Init
  SHARES DiskDefs, SegmentDefs =
  BEGIN OPEN MachineDefs;

  InvalidAddress: PUBLIC ERROR [address: LONG POINTER] = CODE;

  -- Cache of user memory pages

  CoreSegmentObject: TYPE = RECORD [
    address: POINTER,
    lastused: CARDINAL,
    mempage: CARDINAL,
    inuse: BOOLEAN,
    dirty: BOOLEAN,
    segment: SegmentDefs.FileSegmentHandle];

  CoreSegment: TYPE = POINTER TO CoreSegmentObject;

  maxsegments: CARDINAL = 4;        -- number of pages to keep in core

  CS: ARRAY [0..maxsegments) OF CoreSegmentObject;

  CurrentUseValue: CARDINAL;
  data: State.GSHandle ← State.GetGS[];
  CoreFile: SegmentDefs.FileHandle;

  DaListItem: TYPE = RECORD [
    link: POINTER TO DaListItem, base: vDA, count: CARDINAL];

  DAs: POINTER TO DaListItem ← NIL;

  DiskAddresses: PROCEDURE [page: CARDINAL] RETURNS [vDA] =
    BEGIN
    curBase: CARDINAL ← 0;
    FOR p: POINTER TO DaListItem ← DAs, p.link UNTIL p = NIL DO
      IF page < curBase + p.count THEN RETURN[[p.base+(page-curBase)]];
      curBase ← curBase + p.count;
      ENDLOOP;
    RETURN[eofDA]
    END;

  InitCoreCache: PROCEDURE [file: SegmentDefs.FileHandle] =
    BEGIN
    i: CARDINAL;
    CurrentUseValue ← 0;
    FOR i IN [0..maxsegments) DO CS[i].inuse ← FALSE ENDLOOP;
    CoreFile ← file;
    SegmentDefs.LockFile[CoreFile];
    END;

  
  Lengthen: PUBLIC PROCEDURE [p: POINTER] RETURNS [LONG POINTER] =
    BEGIN OPEN Inline, MachineDefs;
    mds: LONG CARDINAL = LongMult[data.mds, PageSize];
    lp: LongNumber ← [num[lowbits: LOOPHOLE[p, CARDINAL], highbits: 0]];
    RETURN[IF p = NIL THEN NIL ELSE LOOPHOLE[lp.lc+mds]]
    END;

  FlushCoreCache: PUBLIC PROCEDURE =
    BEGIN OPEN SegmentDefs;
    i: CARDINAL ← 0;
    cs: CoreSegment;
    FOR i IN [0..maxsegments) DO
      cs←@CS[i];
      IF cs.inuse THEN FlushCS[cs];
      ENDLOOP;
    CurrentUseValue←0;
    END;


  FlushCS: PROCEDURE [cs: CoreSegment] =
    BEGIN OPEN SegmentDefs, cs;
    IF dirty THEN segment.write ← TRUE;
    Unlock[segment];
    DeleteFileSegment[segment];
    inuse ← FALSE;
    END;

  
  NewSwateeSegment: PROCEDURE [mempage: CARDINAL, cs: CoreSegment] =
    BEGIN OPEN SegmentDefs;
    filepage: CARDINAL = SELECT mempage FROM
      IN[2..253] => mempage, 1 => 254, ENDCASE => 255;
    seg: FileSegmentHandle = NewFileSegment[CoreFile, filepage, 1, Read];
    SetFileSegmentDA[seg, DiskAddresses[filepage]];
    MakeSwappedIn[seg, DefaultBase, HardUp];
    cs↑ ← [
      address: FileSegmentAddress[seg],
      inuse: TRUE,
      dirty: FALSE,
      lastused:,
      mempage: mempage,
      segment: seg];
    RETURN
    END;


  GetCS: PROCEDURE [mempage: CARDINAL] RETURNS [sp: CoreSegment] =
    BEGIN
    minUseVal: CARDINAL ← CurrentUseValue;
    minUseIndex: CARDINAL ← 0;
    i: CARDINAL;

    BEGIN
    FOR i IN [0..maxsegments) DO
      sp ← @CS[i];
      IF ~sp.inuse THEN GO TO newseg;
      IF sp.mempage = mempage THEN EXIT;
      IF sp.lastused < minUseVal THEN
	BEGIN minUseVal←sp.lastused; minUseIndex←i END;
      REPEAT FINISHED =>
	BEGIN
	FOR i IN [0..maxsegments) DO
	  CS[i].lastused ← CS[i].lastused - minUseVal;
	  ENDLOOP;
	CurrentUseValue ← CurrentUseValue - minUseVal;
	FlushCS[sp ← @CS[minUseIndex]];
	GO TO newseg;
	END
      ENDLOOP;
    EXITS newseg =>
      BEGIN
      cso: CoreSegmentObject;
      IF mempage IN [0..253] THEN NewSwateeSegment[mempage, @cso]
      ELSE ERROR;
      FOR i IN [0..maxsegments) DO
	sp ← @CS[i];
	IF ~sp.inuse THEN BEGIN sp↑ ← cso; EXIT END;
	REPEAT FINISHED => ERROR
	ENDLOOP;
      END;
    END;
    sp.lastused ← CurrentUseValue ← CurrentUseValue+1;
--    CacheSwap.proc ← FlushCoreCache;
    RETURN[sp];
    END;
  
  LongREAD: PUBLIC PROCEDURE [loc: LONG POINTER] RETURNS [val: UNSPECIFIED] =
    BEGIN OPEN Mopcodes;
    ReadMEMORY: PROCEDURE [LONG POINTER] RETURNS [UNSPECIFIED] =
      MACHINE CODE BEGIN zPOP; zR0 END;
    mempage, offset: CARDINAL;
    [mempage, offset] ← Inline.LongDivMod[LOOPHOLE[loc],PageSize];
    SELECT mempage FROM
      > 255 => RETURN[ReadVM[loc]];
      > 253 => RETURN[ReadMEMORY[loc]];
      ENDCASE => RETURN [(GetCS[mempage].address + offset)↑];
    END;

  
  LongWRITE: PUBLIC PROCEDURE [loc: LONG POINTER, val: UNSPECIFIED] =
    BEGIN
    WriteMEMORY: PROCEDURE [UNSPECIFIED, LONG POINTER] =
      MACHINE CODE BEGIN Mopcodes.zPOP; Mopcodes.zW0 END;
    mempage, offset: CARDINAL;
    [mempage, offset] ← Inline.LongDivMod[LOOPHOLE[loc],PageSize];
    SELECT mempage FROM
      > 255 => WriteVM[loc,val];
      > 253 => WriteMEMORY[val, loc];
      ENDCASE =>
	BEGIN
	s: CoreSegment ← GetCS[mempage];
  	(s.address + offset)↑ ← val;
	s.dirty ← TRUE;
	END;
    RETURN
    END;

  LongCopyREAD: PUBLIC PROCEDURE [
    from: LONG POINTER, nwords: CARDINAL, to: LONG POINTER] =
    BEGIN
    i: CARDINAL;
    FOR i IN [0..nwords) DO (to+i)↑ ← LongREAD[from+i]; ENDLOOP;
    RETURN
    END;
  
  LongCopyWRITE: PUBLIC PROCEDURE [
    from: LONG POINTER, nwords: CARDINAL, to: LONG POINTER] =
    BEGIN
    i: CARDINAL;
    FOR i IN [0..nwords) DO LongWRITE[to+i, (from+i)↑]; ENDLOOP;
    RETURN
    END;

  CheckPointer: PROCEDURE [p: LONG POINTER] =
    BEGIN OPEN Inline;
    table: POINTER TO SwapperOps.SystemTable = data.ESV.tables;
    regions: POINTER TO SwapperOps.RegionTable ← 
      DebugOps.ShortREAD[@table.regions];
    index: CARDINAL ← HighHalf[p];
    IF index > LAST[SegmentDefs.BankIndex] OR
      DebugOps.ShortREAD[@regions[LOOPHOLE[index]]] = NIL THEN
	ERROR InvalidAddress[p];
    END;
  
  ReadVM: PROCEDURE [p: LONG POINTER] RETURNS [UNSPECIFIED] =
    BEGIN
    RBL: PROC [LONG POINTER] RETURNS [UNSPECIFIED] =
      MACHINE CODE BEGIN Mopcodes.zRBL, 0 END;
    CheckPointer[p];
    RETURN[RBL[p]];
    END;
  
  WriteVM: PROCEDURE [p: LONG POINTER, v: UNSPECIFIED] =
    BEGIN OPEN Mopcodes;
    WBL: PROC [UNSPECIFIED, LONG POINTER] = MACHINE CODE BEGIN zWBL, 0 END;
    CheckPointer[p];
    WBL[v, p]
    END;
  
  -- initialization

  Files: PUBLIC PROCEDURE =
    BEGIN OPEN DiskDefs, SegmentDefs;
    fh: FileHandle ← data.debuggeeFH;
    run: POINTER TO DaListItem ← Storage.Node[SIZE[DaListItem]];
    runStart: INTEGER;
    i: INTEGER ← 0;
    p: POINTER TO ARRAY [-1..256] OF vDA ← Storage.Node[258];
    diskrequest: DiskRequest ← [
      ca: Storage.Pages[1],
      fixedCA: TRUE,
      da: @p[0],
      fp: @fh.fp,
      firstPage: 0,
      lastPage: 255,
      action: ReadD,
      lastAction: ReadD,
      signalCheckError: FALSE,
      option: update[BFSDefs.GetNextDA]];
    p[-1] ← fillinDA;
    Inline.COPY[from: @p[-1], to: @p[0], nwords: 257];
    p[0] ← fh.fp.leaderDA;
    [] ← BFSDefs.ActOnPages[LOOPHOLE[@diskrequest]];
    Storage.FreePages[diskrequest.ca];
    DAs ← run;
    run.link ← NIL; -- cross jump
    run.base ← p[runStart ← i];
    DO
      WHILE i <= 255 AND p[i+1] = p[i]+1 DO i ← i + 1 ENDLOOP;
      run.count ← i - runStart + 1;
      IF i > 255 THEN EXIT;
      i ← i + 1;
      run.link ← Storage.Node[SIZE[DaListItem]];
      run ← run.link;
      run.link ← NIL;
      run.base ← p[runStart ← i];
      ENDLOOP;
    Storage.Free[p];
    InitCoreCache[fh];
    END;

  
  END...