-- SegmentsB.Mesa  Edited by Sandman on September 4, 1980  6:00 PM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoDefs USING [MaxVMPage, PageNumber, PageSize, PagesPerMDS],
  AltoFileDefs USING [eofDA, vDA],
  InlineDefs USING [COPY, DIVMOD, HighHalf, LongDiv, LowHalf],
  NucleusOps USING [],
  ProcessDefs USING [DisableInterrupts, EnableInterrupts],
  Region USING [Handle, Index, Page, PagesPerRegion],
  SegmentDefs USING [
    AccessOptions, AddressFromPage, AllocInfo, DataSegmentAddress,
    DataSegmentHandle, DefaultBase, DefaultMDSBase, DefaultPages,
    EasyUp, FileAccessError, FileHandle, FileHint, FileSegmentAddress,
    FileSegmentHandle, GetEndOfFile, InvalidSegmentSize,
    LongAddressFromPage, MakeSwappedIn, MaxRefs, NewFileSegment,
    Object, ObjectHandle, ObjectType, OpenFile, PageCount,
    PageNumber, RefCount, ReleaseFile, SegCount, SegLockCount, SegmentHandle,
    SegmentLocation, SegmentType, SwapError, SwapIn, SwapOut, SwapUp, Unlock, VMtoSegment],
  SwapperOps USING [
    EnumerateObjects, LiberateObject, MapVM, mdsIndex, PositionSeg, regions, UpdateVM,
    ValidateObject];

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

  BEGIN OPEN AltoFileDefs, SwapperOps, SegmentDefs;

  -- Window Segments (such as they are)


  DeleteFileSegment: PUBLIC PROCEDURE [seg: FileSegmentHandle] =
    BEGIN
    file: FileHandle ← seg.file;
    SwapOut[seg];
    LiberateObject[seg];
    file.segcount ← file.segcount - 1;
    IF file.segcount = 0 THEN ReleaseFile[file];
    RETURN
    END;

  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;

  GetFileSegmentDA: PUBLIC PROCEDURE [seg: FileSegmentHandle] RETURNS [vDA] =
    BEGIN
    WITH s: seg SELECT FROM
      disk => BEGIN [] ← SwapperOps.PositionSeg[seg, FALSE]; RETURN[s.hint.da]; END;
      ENDCASE;
    RETURN[AltoFileDefs.eofDA]
    END;

  SetFileSegmentDA: PUBLIC PROCEDURE [seg: FileSegmentHandle, da: vDA] =
    BEGIN
    WITH s: seg SELECT FROM disk => s.hint ← FileHint[da, s.base]; ENDCASE;
    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;

  ChangeDataToFileSegment: PUBLIC PROCEDURE [
    dataseg: DataSegmentHandle, fileseg: FileSegmentHandle] =
    BEGIN
    IF dataseg.pages # fileseg.pages OR ~fileseg.write OR fileseg.swappedin OR
      fileseg.file.swapcount = MaxRefs THEN SIGNAL SwapError[fileseg];
    IF ~fileseg.file.open THEN OpenFile[fileseg.file];
    ProcessDefs.DisableInterrupts[];
    fileseg.swappedin ← TRUE;
    fileseg.lock ← fileseg.lock + 1;
    fileseg.file.swapcount ← fileseg.file.swapcount + 1;
    SwapperOps.UpdateVM[
      base: fileseg.VMpage ← dataseg.VMpage, pages: fileseg.pages, seg: fileseg];
    ProcessDefs.EnableInterrupts[];
    SwapperOps.LiberateObject[dataseg];
    END;

  -- Simplified Data Segments


  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;

  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;

  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;

  InvalidLongPointer: PUBLIC ERROR [lp: LONG UNSPECIFIED] = CODE;

  ValidateLongPointer: PUBLIC PROCEDURE [a: LONG UNSPECIFIED] =
    BEGIN
    page: PageNumber;
    index: Region.Index;
    IF InlineDefs.HighHalf[a] > LAST[Region.Index] THEN
      ERROR InvalidLongPointer[a];
    page ← InlineDefs.LongDiv[a, AltoDefs.PageSize];
    [quotient: index] ← InlineDefs.DIVMOD[page, Region.PagesPerRegion];
    IF regions[index] = NIL THEN ERROR InvalidLongPointer[a];
    END;

  LongVMtoSegment: PUBLIC PROCEDURE [a: LONG POINTER] RETURNS [SegmentHandle] =
    BEGIN
    relPage: Region.Page;
    ValidateLongPointer[a];
    relPage ← PageFromAddress[InlineDefs.LowHalf[a]];
    RETURN[regions[InlineDefs.HighHalf[a]].status[relPage].seg]
    END;

  LongVMtoFileSegment: PUBLIC PROCEDURE [a: LONG POINTER]
    RETURNS [FileSegmentHandle] =
    BEGIN
    seg: SegmentHandle ← LongVMtoSegment[a];
    IF seg = NIL THEN RETURN[NIL];
    WITH s: seg SELECT FROM file => RETURN[@s]; ENDCASE;
    RETURN[NIL];
    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.