-- SegmentsB.Mesa  Edited by Sandman on July 1, 1980  11:14 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoDefs USING [MaxVMPage, PageNumber, PageSize, PagesPerMDS],
  AltoFileDefs USING [eofDA, vDA],
  InlineDefs USING [COPY],
  NucleusOps USING [],
  Region USING [Handle, Index, Page, PagesPerRegion, PageStatus],
  SegmentDefs USING [
    AccessOptions, AddressFromPage, AllocInfo, DataSegmentAddress,
    DataSegmentHandle, DefaultBase, DefaultMDSBase, DefaultPages,
    DeleteDataSegment, EasyDown, EasyUp, FileAccessError, FileHandle, FileHint,
    FileSegmentAddress, FileSegmentHandle, GetEndOfFile, HardDown,
    InvalidSegmentSize, LongAddressFromPage, LongVMtoSegment, MakeDataSegment,
    MakeSwappedIn, NewFileSegment, Object, ObjectHandle, ObjectType, PageCount,
    PageNumber, RefCount, ReleaseFile, SegCount, SegLockCount, SegmentHandle,
    SegmentLocation, SegmentType, SwapError, SwapIn, SwapOut, SwapUp, Unlock],
  Storage USING [PagesForWords],
  SwapperOps USING [
    EnumerateObjects, MapVM, mdsIndex, PositionSeg, regions, UpdateVM,
    ValidateObject],
  SystemDefs USING [];

SegmentsB: PROGRAM
  IMPORTS SwapperOps, InlineDefs, SegmentDefs, Storage
  EXPORTS SwapperOps, SegmentDefs, SystemDefs, Storage, NucleusOps
  SHARES SegmentDefs =PUBLIC

  BEGIN OPEN AltoFileDefs, SwapperOps, SegmentDefs;

  -- Window Segments (such as they are)


  MoveFileSegment: PUBLIC PROCEDURE [
    seg: FileSegmentHandle, base: PageNumber, pages: PageCount] =
    BEGIN
    ValidateObject[seg];
    IF base = DefaultBase THEN base ← 1;
    IF pages = DefaultPages THEN pages ← GetEndOfFile[seg.file].page - base + 1;
    IF pages ~IN (0..AltoDefs.PagesPerMDS] THEN ERROR InvalidSegmentSize[pages];
    SwapOut[seg];
    seg.base ← base;
    seg.pages ← pages;
    RETURN
    END;

  MapFileSegment: PROCEDURE [
    seg: FileSegmentHandle, file: FileHandle, base: PageNumber] =
    BEGIN
    wasin, waswrite: BOOLEAN;
    old: FileHandle = seg.file;
    ValidateObject[seg];
    IF ~old.read THEN ERROR FileAccessError[old];
    IF ~file.write THEN ERROR FileAccessError[file];
    IF base = DefaultBase THEN base ← 1;
    wasin ← seg.swappedin;
    waswrite ← seg.write;
    IF ~wasin THEN SwapIn[seg];
    old.swapcount ← old.swapcount - 1;
    old.segcount ← old.segcount - 1;
    seg.file ← file;
    seg.base ← base;
    WITH s: seg SELECT FROM disk => s.hint ← FileHint[eofDA, 0]; ENDCASE;
    seg.write ← TRUE;
    file.segcount ← file.segcount + 1;
    file.swapcount ← file.swapcount + 1;
    IF wasin OR ~waswrite THEN SwapUp[seg];
    seg.write ← waswrite;
    IF ~wasin THEN BEGIN Unlock[seg]; SwapOut[seg] END;
    IF old.segcount = 0 THEN ReleaseFile[old];
    RETURN
    END;

  BootFileSegment: PROCEDURE [
    file: FileHandle, base: PageNumber, pages: PageCount, access: AccessOptions,
    addr: POINTER] RETURNS [seg: FileSegmentHandle] =
    BEGIN
    seg ← NewFileSegment[file, base, pages, access];
    IF addr # NIL THEN
      BEGIN
      seg.VMpage ← PageFromAddress[addr];
      seg.swappedin ← TRUE;
      seg.lock ← seg.lock + 1;
      file.swapcount ← file.swapcount + 1;
      SwapperOps.UpdateVM[seg.VMpage, pages, seg];
      END;
    RETURN
    END;

  -- Segment Initialization


  CopyDataToFileSegment: PROCEDURE [
    dataseg: DataSegmentHandle, fileseg: FileSegmentHandle] =
    BEGIN
    IF dataseg.pages # fileseg.pages THEN SwapError[fileseg];
    IF fileseg.swappedin OR fileseg.loc = remote THEN
      MoveContents[
	seg: fileseg, from: DataSegmentAddress[dataseg],
	to: FileSegmentAddress[fileseg]]
    ELSE
      WITH s: fileseg SELECT FROM
	disk =>
	  BEGIN
	  s.VMpage ← dataseg.VMpage;
	  IF s.hint.page # s.base OR s.hint.da = eofDA THEN
	    [] ← PositionSeg[@s, FALSE];
	  MapVM[@s, WriteD];
	  END;
	ENDCASE;
    END;

  CopyFileToDataSegment: PROCEDURE [
    fileseg: FileSegmentHandle, dataseg: DataSegmentHandle] =
    BEGIN
    IF dataseg.pages # fileseg.pages THEN SwapError[fileseg];
    IF fileseg.swappedin OR fileseg.loc = remote THEN
      MoveContents[
	seg: fileseg, from: FileSegmentAddress[fileseg],
	to: DataSegmentAddress[dataseg]]
    ELSE
      WITH s: fileseg SELECT FROM
	disk =>
	  BEGIN
	  s.VMpage ← dataseg.VMpage;
	  IF (s.hint.page # s.base OR s.hint.da = eofDA) AND PositionSeg[@s, TRUE]
	    AND s.pages = 1 THEN NULL
	  ELSE MapVM[@s, ReadD];
	  END;
	ENDCASE;
    END;

  MoveContents: PROCEDURE [seg: FileSegmentHandle, from, to: POINTER] =
    BEGIN
    waslocked: BOOLEAN = seg.lock # 0;
    SwapIn[seg];
    InlineDefs.COPY[from: from, to: to, nwords: seg.pages*AltoDefs.PageSize];
    IF ~waslocked THEN
      BEGIN Unlock[seg]; IF seg.loc = remote THEN SwapOut[seg]; END;
    RETURN
    END;

  -- Simplified Data Segments


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

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

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

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

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

  SegmentSize: 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: PROCEDURE [base: POINTER] =
    BEGIN
    seg: DataSegmentHandle = VMtoDataSegment[base];
    IF seg # NIL THEN DeleteDataSegment[seg];
    RETURN
    END;

  EnumerateFileSegments: PROCEDURE [
    proc: PROCEDURE [FileSegmentHandle] RETURNS [BOOLEAN]]
    RETURNS [FileSegmentHandle] =
    BEGIN

    CheckSegment: PROCEDURE [seg: SegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      RETURN[WITH s: seg SELECT FROM file => proc[@s], ENDCASE => FALSE]
      END;

    RETURN[LOOPHOLE[EnumerateObjects[segment, LOOPHOLE[CheckSegment]]]];
    END;

  VMtoDataSegment: 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;

  VMtoFileSegment: PROCEDURE [a: POINTER] RETURNS [FileSegmentHandle] =
    BEGIN
    seg: SegmentHandle ← VMtoSegment[a];
    IF seg = NIL THEN RETURN[NIL];
    WITH s: seg SELECT FROM file => RETURN[@s]; ENDCASE;
    RETURN[NIL];
    END;

  VMtoSegment: 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;

  SegmentAddress: PROCEDURE [seg: SegmentHandle] RETURNS [POINTER] =
    BEGIN
    WITH s: seg SELECT FROM file => IF ~s.swappedin THEN RETURN[NIL]; ENDCASE;
    RETURN[AddressFromPage[seg.VMpage]]
    END;

  EnumerateDataSegments: PROCEDURE [
    proc: PROCEDURE [DataSegmentHandle] RETURNS [BOOLEAN]]
    RETURNS [DataSegmentHandle] =
    BEGIN

    CheckSegment: PROCEDURE [seg: SegmentHandle] RETURNS [BOOLEAN] =
      BEGIN
      RETURN[WITH s: seg SELECT FROM data => proc[@s], ENDCASE => FALSE]
      END;

    RETURN[LOOPHOLE[EnumerateObjects[segment, LOOPHOLE[CheckSegment]]]];
    END;

  PageFromAddress: PROCEDURE [a: POINTER] RETURNS [page: PageNumber] =
    BEGIN
    RETURN[
      SwapperOps.mdsIndex*Region.PagesPerRegion +
	LOOPHOLE[a, CARDINAL]/AltoDefs.PageSize]
    END;

  PageAvailable: PROCEDURE [page: PageNumber, info: AllocInfo] RETURNS [BOOLEAN] =
    BEGIN
    r: Region.Handle;
    IF (r ← GetRegion[page]) = NIL THEN RETURN[FALSE];
    RETURN[r.available[page MOD Region.PagesPerRegion, info]];
    END;

  PagesAvailable: PROCEDURE [base: PageNumber, pages: PageCount, info: AllocInfo]
    RETURNS [BOOLEAN] =
    BEGIN
    FOR base IN [base..base + pages) DO
      IF ~PageAvailable[base, info] THEN RETURN[FALSE] ENDLOOP;
    RETURN[TRUE];
    END;

  GetRegion: PROCEDURE [page: PageNumber] RETURNS [r: Region.Handle] =
    BEGIN
    r ← NIL;
    IF CARDINAL[page] <= AltoDefs.MaxVMPage THEN
      r ← regions[CARDINAL[page] MOD Region.PagesPerRegion];
    RETURN
    END;

  InvalidVMPage: ERROR [page: UNSPECIFIED] = CODE;

  ValidateVMPage: PROCEDURE [page: UNSPECIFIED] =
    BEGIN IF GetRegion[page] = NIL THEN ERROR InvalidVMPage[page]; END;

  LongSegmentAddress: PROCEDURE [seg: SegmentHandle] RETURNS [LONG POINTER] =
    BEGIN
    WITH s: seg SELECT FROM file => IF ~s.swappedin THEN RETURN[NIL]; ENDCASE;
    RETURN[LongAddressFromPage[seg.VMpage]]
    END;

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

  LongDataSegmentAddress: PROCEDURE [seg: DataSegmentHandle]
    RETURNS [LONG POINTER] = LOOPHOLE[LongSegmentAddress];

  LongFileSegmentAddress: PROCEDURE [seg: FileSegmentHandle]
    RETURNS [LONG POINTER] = LOOPHOLE[LongSegmentAddress];

  END.