-- SegmentsA.Mesa  Edited by Sandman on September 4, 1980  5:59 PM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoDefs USING [MaxMDSPage, PageSize],
  AltoFileDefs USING [CFA, eofDA, FA, FP, vDA],
  DiskDefs USING [DiskCheckError, DiskPageDesc, DiskRequest, nSectors, SwapPages],
  InlineDefs USING [BITAND, LongCOPY],
  NucleusOps USING [],
  Region USING [Index, Page],
  SegmentDefs USING [
    AccessOptions, AddressFromPage, AllocInfo, Append, ChangeDataToFileSegment,
    DataSegmentAddress, DataSegmentHandle, DefaultAccess, DefaultBase,
    DefaultMDSBase, DefaultPages, DeleteDataSegment, DeleteFileSegment, EasyDown,
    EnumerateFileSegments, FileAccessError, FileError, FileHandle, FileHint,
    FileSegmentClass, FileSegmentHandle, GetEndOfFile, HardDown, LongAddressFromPage,
    MakeDataSegment, MaxSegs, NewDataSegment, Object, ObjectHandle, ObjectType,
    PageCount, PageNumber, Read, SegCount, SegLockCount, SegmentHandle,
    SegmentLocation, SegmentType, SetFileAccess, SwapError, Unlock, Write],
  Storage USING [PagesForWords],
  SwapperOps USING [
    AllocateObject, MapVM, mdsIndex, regions, ValidateObject],
  SystemDefs USING [];

SegmentsA: PROGRAM
  IMPORTS SwapperOps, DiskDefs, InlineDefs, SegmentDefs, Storage
  EXPORTS SwapperOps, SegmentDefs, NucleusOps, Storage, SystemDefs
  SHARES SegmentDefs =
  BEGIN OPEN AltoFileDefs, SwapperOps, SegmentDefs;

  InvalidSegmentSize: PUBLIC SIGNAL [pages: PageCount] = CODE;

  NewFileSegment: PUBLIC PROCEDURE [
    file: FileHandle, base: PageNumber, pages: PageCount, access: AccessOptions]
    RETURNS [seg: FileSegmentHandle] =
    BEGIN OPEN InlineDefs;
    IF access = DefaultAccess THEN access ← Read;
    IF file.segcount = MaxSegs THEN ERROR FileError[file];
    IF BITAND[access, Append] # 0 THEN ERROR FileAccessError[file];
    IF base = DefaultBase THEN base ← 1;
    IF pages = DefaultPages THEN pages ← GetEndOfFile[file].page - base + 1;
    IF pages ~IN (0..AltoDefs.MaxMDSPage + 1] THEN
      ERROR InvalidSegmentSize[pages];
    SetFileAccess[file, access];
    seg ← LOOPHOLE[AllocateObject[SIZE[file segment Object]]];
    seg↑ ← Object[
      busy: FALSE,
      body: segment[
      VMpage: 0,
      info: file[
      swappedin: FALSE, write: BITAND[access, Write] # 0, class: other,
      inuse: FALSE, lock: 0, file: file, base: base, pages: pages,
      location: disk[FileHint[eofDA, 0]]]]];
    file.segcount ← file.segcount + 1;
    RETURN
    END;

  FileSegmentAddress: PUBLIC PROCEDURE [seg: FileSegmentHandle]
    RETURNS [POINTER] =
    BEGIN
    IF ~seg.swappedin THEN ERROR SwapError[seg];
    RETURN[AddressFromPage[seg.VMpage]]
    END;

  SwapUp: PUBLIC PROCEDURE [seg: FileSegmentHandle] =
    BEGIN OPEN AltoDefs, seg;
    SwapperOps.ValidateObject[seg];
    IF swappedin AND write THEN
      WITH s: seg SELECT FROM
	disk =>
	  IF s.VMpage <= MaxMDSPage THEN
	    BEGIN
	    IF s.hint.page # base OR s.hint.da = AltoFileDefs.eofDA THEN
	      [] ← PositionSeg[@s, FALSE];
	    SwapperOps.MapVM[@s, WriteD];
	    END
	  ELSE
	    BEGIN
	    pages: CARDINAL = s.pages;
	    mdsFileSeg: FileSegmentHandle ← NewFileSegment[
	      file, s.base, pages, Read + Write];
	    mdsDataSeg: DataSegmentHandle ← MakeDataSegment[
	      DefaultMDSBase, pages, EasyDown];
	    InlineDefs.LongCOPY[
	      from: LongAddressFromPage[s.VMpage],
	      to: LongAddressFromPage[mdsDataSeg.VMpage], nwords: pages*PageSize];
	    SegmentDefs.ChangeDataToFileSegment[mdsDataSeg, mdsFileSeg];
	    Unlock[mdsFileSeg];
	    DeleteFileSegment[mdsFileSeg];
	    END;
	remote => s.proc[@s, 1];
	ENDCASE;
    RETURN
    END;

  -- Segment Positioning


  PositionSeg: PUBLIC PROCEDURE [seg: FileSegmentHandle, useseg: BOOLEAN]
    RETURNS [BOOLEAN] =
    BEGIN -- returns TRUE if it read a non-null page into the segment.
    cfa: CFA;
    buf: DataSegmentHandle;
    buffer: POINTER;
    WITH s: seg SELECT FROM
      disk =>
	BEGIN
	IF s.hint.da = eofDA AND s.base > 8 AND s.file.segcount > 1 THEN
	  FindSegHint[@s];
	IF s.hint.da = eofDA OR s.hint.page # s.base THEN
	  BEGIN
	  buffer ←
	    IF useseg THEN AddressFromPage[s.VMpage]
	    ELSE DataSegmentAddress[buf ← NewDataSegment[DefaultBase, 1]];
	  cfa.fp ← s.file.fp;
	  cfa.fa ← FA[s.hint.da, s.hint.page, 0];
	  [] ← JumpToPage[
	    @cfa, s.base, buffer !
	    UNWIND => IF ~useseg THEN DeleteDataSegment[buf]];
	  IF ~useseg THEN DeleteDataSegment[buf];
	  IF cfa.fa.page # s.base THEN ERROR SwapError[@s];
	  s.hint ← FileHint[cfa.fa.da, cfa.fa.page];
	  RETURN[useseg AND cfa.fa.byte # 0];
	  END;
	END;
      ENDCASE;
    RETURN[FALSE]
    END;

  FindSegHint: PUBLIC PROCEDURE [seg: FileSegmentHandle] =
    BEGIN
    CheckHint: PROCEDURE [other: FileSegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      WITH o: other SELECT FROM
	disk =>
	  BEGIN
	  IF o.file = seg.file AND o.hint.da # eofDA AND o.hint.page IN
	    (hint.page..seg.base] THEN hint ← o.hint;
	  RETURN[hint.page = seg.base]
	  END;
	ENDCASE;
      RETURN[FALSE]
      END;
    hint: FileHint;
    WITH s: seg SELECT FROM
      disk =>
	BEGIN
	hint ← s.hint;
	[] ← EnumerateFileSegments[CheckHint];
	s.hint ← hint;
	END;
      ENDCASE;
    RETURN
    END;

  -- File Positioning

  jump: INTEGER = 10*DiskDefs.nSectors;

  InvalidFP: PUBLIC SIGNAL [fp: POINTER TO FP] = CODE;

  JumpToPage: PUBLIC PROCEDURE [
    cfa: POINTER TO CFA, page: PageNumber, buf: POINTER]
    RETURNS [prev, next: vDA] =
    BEGIN OPEN DiskDefs;
    desc: DiskPageDesc;
    da: vDA ← cfa.fa.da;
    startpage: PageNumber;
    direction: INTEGER ← 1;
    firstpage: PageNumber ← cfa.fa.page;
    arg: swap DiskRequest ← DiskRequest[
      buf, @da, , , @cfa.fp, TRUE, ReadD, ReadD, TRUE, swap[@desc]];
    BEGIN
    IF da = eofDA THEN GO TO reset;
    SELECT firstpage - page FROM
      <= 0 => NULL;
      = 1, < firstpage/10 => direction ← -1;
      ENDCASE => GO TO reset;
    EXITS reset => BEGIN firstpage ← 0; da ← cfa.fp.leaderDA; END;
    END;
    BEGIN
    ENABLE
      DiskCheckError --[page]-- =>
	BEGIN
	IF page # startpage THEN RESUME;
	IF startpage = 0 THEN GO TO failed;
	firstpage ← 0;
	da ← cfa.fp.leaderDA;
	direction ← 1;
	RETRY;
	END;
    IF da = eofDA THEN GO TO failed;
    startpage ← firstpage;
    UNTIL da = eofDA DO
      arg.firstPage ← firstpage;
      arg.lastPage ←
	IF direction < 0 THEN firstpage ELSE MIN[page, firstpage + jump - 1];
      [] ← SwapPages[@arg];
      IF desc.page = page THEN EXIT;
      da ← IF direction < 0 THEN desc.prev ELSE desc.next;
      firstpage ← desc.page + direction;
      ENDLOOP;
    cfa.fa ← FA[desc.this, desc.page, desc.bytes];
    RETURN[desc.prev, desc.next];
    EXITS failed => ERROR InvalidFP[@cfa.fp];
    END;
    END;

  GetPages: PROCEDURE [npages: CARDINAL, info: AllocInfo] RETURNS [POINTER] =
    BEGIN
    RETURN[DataSegmentAddress[MakeDataSegment[DefaultMDSBase, npages, info]]]
    END;

  AllocatePages: PUBLIC PROCEDURE [npages: CARDINAL] RETURNS [POINTER] =
    BEGIN RETURN[GetPages[npages, EasyDown]] END;

  Pages, AllocateResidentPages: PUBLIC PROC [npages: CARDINAL] RETURNS [POINTER] =
    BEGIN RETURN[GetPages[npages, HardDown]] END;

  AllocateSegment: PUBLIC PROCEDURE [nwords: CARDINAL] RETURNS [POINTER] =
    BEGIN RETURN[GetPages[Storage.PagesForWords[nwords], EasyDown]] END;

  Words, AllocateResidentSegment: PUBLIC PROC [nwords: CARDINAL] RETURNS [POINTER] =
    BEGIN RETURN[GetPages[Storage.PagesForWords[nwords], HardDown]] END;

  SegmentSize: PUBLIC PROCEDURE [base: POINTER] RETURNS [CARDINAL] =
    BEGIN
    seg: DataSegmentHandle = VMtoDataSegment[base];
    RETURN[IF seg = NIL THEN 0 ELSE seg.pages*AltoDefs.PageSize]
    END;

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

  VMtoDataSegment: PUBLIC PROCEDURE [a: POINTER] RETURNS [DataSegmentHandle] =
    BEGIN
    seg: SegmentHandle ← VMtoSegment[a];
    IF seg = NIL THEN RETURN[NIL];
    WITH s: seg SELECT FROM data => RETURN[@s]; ENDCASE;
    RETURN[NIL];
    END;

  VMtoSegment: PUBLIC PROCEDURE [a: POINTER] RETURNS [SegmentHandle] =
    BEGIN
    pg: PageNumber = LOOPHOLE[a, CARDINAL]/AltoDefs.PageSize;
    IF a = NIL THEN RETURN[NIL];
    RETURN[regions[mdsIndex].status[pg].seg];
    END;


  END.