-- File: ImageCache.Mesa by Bruce on July 8, 1980  8:41 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AllocDefs USING [AddSwapStrategy, CantSwap, SwappingProcedure, SwapStrategy],
  AltoDefs USING [LogPageSize, PageNumber, PageSize],
  AltoFileDefs USING [eofDA, fillinDA, vDA],
  BFSDefs USING [ActOnPages, GetNextDA],
  DiskDefs USING [DiskRequest],
  ImageFileInfoDefs USING [],
  ImageFormat USING [FirstImageDataPage, ImageHeader, MapItem],
  InlineDefs USING [BITAND, BITSHIFT, COPY],
  SegmentDefs USING [
    DeleteFileSegment, FileHandle, FileSegmentAddress, FileSegmentHandle,
    GetFileSegmentDA, LockFile, NewFileSegment, Read, SetFileSegmentDA, SwapIn,
    Unlock],
  Storage USING [Pages, FreePages];

ImageCache: PROGRAM
  IMPORTS AllocDefs, BFSDefs, InlineDefs, Storage, SegmentDefs
  EXPORTS ImageFileInfoDefs
  SHARES SegmentDefs =
  BEGIN
  
  -- User's core image management
  
  CoreSegmentObject: TYPE = RECORD [
    segment: SegmentDefs.FileSegmentHandle,
    address: POINTER,
    lastused: CARDINAL,
    page: AltoDefs.PageNumber];
  
  CoreSegment: TYPE = POINTER TO CoreSegmentObject;
  
  maxsegments: CARDINAL = 8; -- number of pages to keep in core
  
  CS: ARRAY [0..maxsegments) OF CoreSegmentObject;
  
  CurrentUseValue: CARDINAL;
  CoreFile: SegmentDefs.FileHandle;
  DAs: ARRAY [-1..256] OF AltoFileDefs.vDA;
  CacheSwap: AllocDefs.SwapStrategy ← AllocDefs.SwapStrategy[
    link:, proc: AllocDefs.CantSwap];
  PageMap: PACKED ARRAY [0..256) OF [0..256);
  
  InvalidPage: PUBLIC SIGNAL [page: AltoDefs.PageNumber] = CODE;
  
  InitImageCache: PROCEDURE [file: SegmentDefs.FileHandle] =
    BEGIN
    i: CARDINAL;
    CurrentUseValue ← 0;
    FOR i IN [0..maxsegments) DO CS[i].segment ← NIL ENDLOOP;
    CoreFile ← file;
    SegmentDefs.LockFile[CoreFile];
    AllocDefs.AddSwapStrategy[@CacheSwap];
    END;
    
  FlushCoreCache: PUBLIC AllocDefs.SwappingProcedure =
    BEGIN OPEN SegmentDefs;
    did: BOOLEAN ← FALSE;
    i: CARDINAL ← 0;
    cs: CoreSegment;
    CacheSwap.proc ← AllocDefs.CantSwap;
    FOR i IN [0..maxsegments) DO
      cs ← @CS[i];
      IF cs.segment # NIL THEN
	BEGIN
	Unlock[cs.segment];
	DeleteFileSegment[cs.segment];
	cs.segment ← NIL;
	did ← TRUE;
	END;
      ENDLOOP;
    CurrentUseValue ← 0;
    RETURN[did]
    END;
    
  NewCoreSegment: PROCEDURE [p: AltoDefs.PageNumber, cs: CoreSegment] =
    BEGIN OPEN SegmentDefs;
    seg: FileSegmentHandle;
    seg ← NewFileSegment[CoreFile, PageMap[p], 1, Read];
    SetFileSegmentDA[seg, DAs[p]];
    SwapIn[seg];
    DAs[p] ← GetFileSegmentDA[seg];
    cs.segment ← seg;
    cs.address ← FileSegmentAddress[seg];
    cs.page ← p;
    END;
    
  GetCS: PROCEDURE [p: AltoDefs.PageNumber] RETURNS [sp: CoreSegment] =
    BEGIN
    minUseVal: CARDINAL ← CurrentUseValue;
    minUseIndex: CARDINAL ← 0;
    i: CARDINAL;
    BEGIN
    FOR i IN [0..maxsegments) DO
      sp ← @CS[i];
      IF sp.segment = NIL THEN GO TO newseg;
      IF sp.page = p 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;
	  sp ← @CS[minUseIndex];
	  SegmentDefs.Unlock[sp.segment];
	  SegmentDefs.DeleteFileSegment[sp.segment];
	  sp.segment ← NIL;
	  GO TO newseg;
	  END
      ENDLOOP;
    EXITS
      newseg =>
	BEGIN
	cso: CoreSegmentObject;
	NewCoreSegment[p, @cso];
	FOR i IN [0..maxsegments) DO
	  sp ← @CS[i];
	  IF sp.segment = NIL THEN BEGIN sp↑ ← cso; EXIT END;
	  REPEAT FINISHED => ERROR
	  ENDLOOP;
	END;
    END;
    sp.lastused ← CurrentUseValue ← CurrentUseValue + 1;
    CacheSwap.proc ← FlushCoreCache;
    RETURN[sp];
    END;
    
  READ: PUBLIC PROCEDURE [a: UNSPECIFIED] RETURNS [UNSPECIFIED] =
    BEGIN OPEN AltoDefs, InlineDefs;
    memory: POINTER = a;
    fp: PageNumber =
      SELECT BITSHIFT[
	a, -LogPageSize] FROM
	IN [2..253] => BITSHIFT[a, -LogPageSize],
	1 => 254,
	0 => 255,
	ENDCASE => 0;
    IF fp = 0 THEN RETURN[memory↑];
    IF PageMap[fp] = 0 THEN SIGNAL InvalidPage[fp];
    RETURN[(GetCS[fp].address + BITAND[a, PageSize - 1])↑];
    END;
    
  CopyRead: PUBLIC PROCEDURE [from, to: POINTER, nwords: CARDINAL] =
    BEGIN
    i: CARDINAL;
    FOR i IN [0..nwords) DO (to + i)↑ ← READ[from + i]; ENDLOOP;
    RETURN
    END;
    
  InvalidImageFile: PUBLIC SIGNAL = CODE;
  
  -- initialization
  
  
  InitializeImageCache: PUBLIC PROCEDURE [seg: SegmentDefs.FileSegmentHandle] =
    BEGIN OPEN AltoFileDefs, DiskDefs, SegmentDefs;
    image: POINTER TO ImageFormat.ImageHeader;
    map: POINTER TO normal ImageFormat.MapItem;
    i, mapIndex: CARDINAL ← 0;
    page, count, imagePage: CARDINAL;
    p: POINTER;
    ImageDAs: ARRAY [-1..256] OF AltoFileDefs.vDA;
    diskrequest: DiskRequest;
    SwapIn[seg];
    image ← FileSegmentAddress[seg];
    IF image.prefix.type = checkfile THEN SIGNAL InvalidImageFile;
    diskrequest ← DiskRequest[
      ca: Storage.Pages[1], fixedCA: TRUE, da: @ImageDAs[0], fp: @seg.file.fp,
      firstPage: 0, lastPage: 255, action: ReadD, lastAction: ReadD,
      signalCheckError: FALSE, option: update[BFSDefs.GetNextDA]];
    p ← LOOPHOLE[@ImageDAs[0] - 1];
    p↑ ← fillinDA;
    InlineDefs.COPY[from: p, to: p + 1, nwords: 257];
    ImageDAs[0] ← seg.file.fp.leaderDA;
    [] ← BFSDefs.ActOnPages[LOOPHOLE[@diskrequest]];
    Storage.FreePages[diskrequest.ca];
    map ← LOOPHOLE[@image.map[0]];
    imagePage ← ImageFormat.FirstImageDataPage;
    FOR i IN [0..256) DO DAs[i] ← AltoFileDefs.eofDA; PageMap[i] ← 0; ENDLOOP;
    WHILE (map + mapIndex)↑ # ImageFormat.MapItem[0, 0, normal[]] DO
      page ← (map + mapIndex)↑.page;
      count ← (map + mapIndex)↑.count;
      FOR i IN [0..count) DO
	DAs[page + i] ← ImageDAs[imagePage + i];
	PageMap[page + i] ← imagePage + i;
	ENDLOOP;
      imagePage ← imagePage + count;
      mapIndex ← mapIndex + 1;
      ENDLOOP;
    Unlock[seg];
    InitImageCache[seg.file];
    END;
    
  
  END...