-- Miscellaneous.mesa; edited by Sandman, September 2, 1980  10:04 AM
-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  AltoFileDefs USING [TIME],
  BcdDefs USING [NullVersion, VersionID, VersionStamp],
  BitBltDefs USING [AlignedBBTable, BBptr, BBTableSpace, BITBLT],
  ControlDefs USING [
    ControlLink, FrameHandle, GFT, GFTIndex, GlobalFrameHandle, NullEpBase,
    NullGlobalFrame, NullLink, UnboundLink],
  FrameDefs USING [GlobalFrame, SwapInCode, SwapOutCode, UnNew],
  FrameOps USING [CodeHandle, Free, GetReturnFrame, ReleaseCode, SetReturnLink],
  ImageDefs USING [
    AbortMesa, CleanupItem, CleanupMask, CleanupProcedure, StopMesa],
  ImageFormat USING [ImageHeader, VersionID],
  InlineDefs USING [BITAND, COPY, LongNumber],
  MiscDefs USING [DAYTIME],
  NucleusOps USING [Resident],
  ProcessDefs USING [DisableInterrupts, EnableInterrupts],
  Runtime USING [],
  SDDefs USING [SD, sGoingAway],
  SDOps USING [BlockEqualLong, ByteBlockEqualLong, Signal],
  SegmentDefs USING [
    DefaultANYBase, DeleteFileSegment, FileSegmentAddress, FileSegmentHandle,
    MakeSwappedIn, MoveFileSegment, NewFileSegment, Read, SwapIn, Unlock],
  SwapperOps USING [SwapOutCodeSeg],
  SystemDefs USING [],
  TrapDefs USING [ResumeError];

Miscellaneous: PROGRAM
  IMPORTS
    BitBltDefs, FrameDefs, FrameOps, ImageDefs, InlineDefs, MiscDefs, NucleusOps,
    ProcessDefs, SDOps, SegmentDefs, SwapperOps, TrapDefs
  EXPORTS FrameDefs, ImageDefs, MiscDefs, NucleusOps, Runtime, SDOps, SystemDefs
  SHARES ControlDefs =PUBLIC

  BEGIN OPEN ControlDefs;

  DeletedFrame: PROCEDURE [gfi: GFTIndex] RETURNS [BOOLEAN] =
    BEGIN RETURN[GFT[gfi] = [frame: NullGlobalFrame, epbase: NullEpBase]]; END;

  SwapOutCode: PROCEDURE [f: GlobalFrameHandle] =
    BEGIN
    cseg: SegmentDefs.FileSegmentHandle = FrameOps.CodeHandle[f];
    IF cseg # NIL THEN SwapperOps.SwapOutCodeSeg[cseg];
    RETURN
    END;

  LockCode: PROCEDURE [link: UNSPECIFIED] =
    BEGIN FrameDefs.SwapInCode[FrameDefs.GlobalFrame[link]]; RETURN END;

  UnlockCode: PROCEDURE [link: UNSPECIFIED] =
    BEGIN OPEN FrameDefs;
    seg: SegmentDefs.FileSegmentHandle ← FrameOps.CodeHandle[GlobalFrame[link]];
    IF seg # NIL AND seg.lock # 0 THEN SegmentDefs.Unlock[seg];
    RETURN
    END;

  MakeCodeResident: PROCEDURE [f: GlobalFrameHandle] =
    BEGIN OPEN SegmentDefs;
    seg: FileSegmentHandle;
    IF (seg ← FrameOps.CodeHandle[f]) = NIL THEN RETURN;
    IF seg.lock = 0 THEN FrameDefs.SwapOutCode[f];
    MakeSwappedIn[seg, DefaultANYBase, [hard, bottomup, code]];
    RETURN
    END;

  GetCaller: PROCEDURE RETURNS [PROGRAM] =
    BEGIN
    RETURN[LOOPHOLE[FrameOps.GetReturnFrame[].returnlink.frame.accesslink]];
    END;

  IsBound: PROCEDURE [link: UNSPECIFIED] RETURNS [BOOLEAN] =
    BEGIN
    RETURN[link # ControlDefs.UnboundLink AND link # ControlDefs.NullLink];
    END;

  SelfDestruct: PROCEDURE =
    BEGIN
    destructee: FrameHandle ← FrameOps.GetReturnFrame[];
    FrameOps.SetReturnLink[destructee.returnlink];
    FrameDefs.UnNew[FrameDefs.GlobalFrame[destructee]];
    FrameOps.Free[destructee];
    RETURN
    END;

  -- Bcd Version and Time


  Version: PROCEDURE [frame: GlobalFrameHandle, type: {bcd, image}]
    RETURNS [version: BcdDefs.VersionStamp] =
    BEGIN OPEN SegmentDefs;
    codeseg: FileSegmentHandle ← FrameOps.CodeHandle[frame];
    seg: FileSegmentHandle;
    image: POINTER TO ImageFormat.ImageHeader;
    id: CARDINAL;
    p: POINTER TO RECORD [a, pages: CARDINAL];
    IF codeseg = NIL THEN RETURN[BcdDefs.NullVersion];
    seg ← NewFileSegment[codeseg.file, 1, 1, Read];
    SwapIn[seg];
    image ← p ← FileSegmentAddress[seg];
    IF type = image AND image.prefix.versionident # ImageFormat.VersionID THEN
      BEGIN
      base: CARDINAL ← p.pages + 1;
      Unlock[seg];
      MoveFileSegment[seg, base, 1];
      SwapIn[seg];
      image ← FileSegmentAddress[seg];
      END;
    id ← image.prefix.versionident;
    version ←
      IF (id = ImageFormat.VersionID) OR (type = bcd AND id = BcdDefs.VersionID)
      THEN image.prefix.version ELSE BcdDefs.NullVersion;
    Unlock[seg];
    DeleteFileSegment[seg];
    RETURN
    END;

  BcdVersion: PROCEDURE RETURNS [version: BcdDefs.VersionStamp] =
    BEGIN OPEN FrameDefs, FrameOps;
    RETURN[Version[frame: GlobalFrame[GetReturnFrame[]], type: bcd]]
    END;

  GetBcdTime, BcdTime: PROCEDURE RETURNS [LONG CARDINAL] = {
    OPEN FrameDefs, FrameOps;
    RETURN[Version[frame: GlobalFrame[GetReturnFrame[]], type: bcd].time]};

  -- Image Version and Time


  ImageVersion: PROCEDURE RETURNS [version: BcdDefs.VersionStamp] =
    BEGIN OPEN FrameDefs;
    RETURN[Version[frame: GlobalFrame[NucleusOps.Resident], type: image]]
    END;

  GetBuildTime, ImageTime: PROCEDURE RETURNS [LONG CARDINAL] = {
    OPEN FrameDefs;
    RETURN[Version[frame: GlobalFrame[NucleusOps.Resident], type: image].time]};

  CurrentTime: PROCEDURE RETURNS [LONG CARDINAL] =
    BEGIN
    time: AltoFileDefs.TIME ← MiscDefs.DAYTIME[];
    RETURN[
      LOOPHOLE[InlineDefs.LongNumber[
	num[highbits: time.high, lowbits: time.low]]]];
    END;

  Even: PROCEDURE [n: CARDINAL] RETURNS [CARDINAL] = {RETURN[n + (n MOD 2)]};

  Quad: PROCEDURE [n: CARDINAL] RETURNS [CARDINAL] = {
    RETURN[n + InlineDefs.BITAND[-n, 3B]]};

  SetBlock: PROCEDURE [p: POINTER, v: UNSPECIFIED, l: CARDINAL] =
    BEGIN
    IF l = 0 THEN RETURN;
    p↑ ← v;
    InlineDefs.COPY[from: p, to: p + 1, nwords: l - 1];
    RETURN
    END;

  PPA: TYPE = POINTER TO PACKED ARRAY [0..0) OF [0..377B];

  BlockEqualCodeLong: PUBLIC PROCEDURE [
    p1: LONG POINTER, n: CARDINAL, offset: CARDINAL] RETURNS [BOOLEAN] =
    BEGIN
    result: BOOLEAN;
    p2: LONG POINTER;
    frame: GlobalFrameHandle = FrameOps.GetReturnFrame[].accesslink;
    FrameDefs.SwapInCode[frame];
    p2 ←
      IF frame.code.highByte = 0 THEN frame.code.longbase + offset
      ELSE frame.code.shortbase + offset;
    result ← SDOps.BlockEqualLong[p1: p1, n: n, p2: p2];
    FrameOps.ReleaseCode[frame];
    RETURN[result]
    END;

  ByteBlockEqualCodeLong: PUBLIC PROCEDURE [
    p1: LONG POINTER, n: CARDINAL, offset: CARDINAL] RETURNS [BOOLEAN] =
    BEGIN
    result: BOOLEAN;
    p2: LONG POINTER;
    frame: GlobalFrameHandle = FrameOps.GetReturnFrame[].accesslink;
    FrameDefs.SwapInCode[frame];
    p2 ←
      IF frame.code.highByte = 0 THEN frame.code.longbase + offset
      ELSE frame.code.shortbase + offset;
    result ← SDOps.ByteBlockEqualLong[p1: p1, n: n, p2: p2];
    FrameOps.ReleaseCode[frame];
    RETURN[result]
    END;

  ByteBlt: PUBLIC PROCEDURE [to, from: PPA, toByte, fromByte, nBytes: CARDINAL] =
    BEGIN
    IF nBytes = 0 THEN RETURN;
    IF (toByte MOD 2) # 0 THEN
      BEGIN
      to[toByte] ← from[fromByte];
      toByte ← toByte + 1;
      fromByte ← fromByte + 1;
      nBytes ← nBytes - 1;
      END;
    IF (fromByte MOD 2) = 0 THEN -- fast case: both are word aligned
      BEGIN
      words: CARDINAL = nBytes/2;
      InlineDefs.COPY[to: to + toByte/2, from: from + fromByte/2, nwords: words];
      IF 2*words # nBytes THEN to[toByte + 2*words] ← from[fromByte + 2*words];
      -- move the last byte

      END
    ELSE -- slow case: have to ripple things
      BEGIN
      lineWidth: CARDINAL = 16; -- words per line: controls interrupt latency
      bba: BitBltDefs.BBTableSpace;
      bbt: BitBltDefs.BBptr ← BitBltDefs.AlignedBBTable[@bba];
      lines, tail: CARDINAL;
      bbt↑ ←
	[sourcetype: block, function: replace, dbca: to + toByte/2,
	  dbmr: lineWidth, dlx: 0, dw: 16*lineWidth, sbca: from + fromByte/2,
	  sbmr: lineWidth, slx: 8];
      -- BITBLT is not interruptable except at the end of each scan line,
      -- so we break things up into chunks in order to maintain reasonable
      -- interrupt latency for the IO devices.  It takes about 200microsec
      -- to move 50 bytes with the display off.
      tail ← nBytes MOD (2*lineWidth); -- bytes left to move with second BitBlt
      lines ← nBytes/(2*lineWidth);
      -- This "moves" a rectangle that is lineWidth words wide by as many
      -- lines high as will fit.  NB: It cheats and actually reads a byte
      -- from beyond the edge of the rectangle.  This is not really legal,
      -- but works out OK for any reasonable inplementation of BitBlt.
      bbt.dty ← bbt.sty ← 0;
      bbt.dw ← 16*lineWidth;
      bbt.dh ← lines;
      BitBltDefs.BITBLT[bbt];
      -- This BitBlt will move one line that is less than lineWidth words wide.
      bbt.dty ← bbt.sty ← lines;
      bbt.dw ← 8*tail;
      bbt.dh ← 1;
      BitBltDefs.BITBLT[bbt];
      END;
    END;

  -- procedure lists

  UserCleanupList: POINTER TO ImageDefs.CleanupItem ← NIL;

  AddCleanupProcedure: PROCEDURE [item: POINTER TO ImageDefs.CleanupItem] =
    BEGIN
    ProcessDefs.DisableInterrupts[];
    RemoveCleanupProcedure[item];
    item.link ← UserCleanupList;
    UserCleanupList ← item;
    ProcessDefs.EnableInterrupts[];
    END;

  RemoveCleanupProcedure: PROCEDURE [item: POINTER TO ImageDefs.CleanupItem] =
    BEGIN
    prev, this: POINTER TO ImageDefs.CleanupItem;
    IF UserCleanupList = NIL THEN RETURN;
    ProcessDefs.DisableInterrupts[];
    prev ← this ← UserCleanupList;
    IF this = item THEN UserCleanupList ← this.link
    ELSE
      UNTIL (this ← this.link) = NIL DO
	IF this = item THEN BEGIN prev.link ← this.link; EXIT END;
	prev ← this;
	ENDLOOP;
    ProcessDefs.EnableInterrupts[];
    END;

  UserCleanupProc: ImageDefs.CleanupProcedure =
    BEGIN -- all interrupts off if why = finish or abort
    this, next: POINTER TO ImageDefs.CleanupItem;
    this ← UserCleanupList;
    UserCleanupList ← NIL;
    WHILE this # NIL DO
      next ← this.link;
      IF InlineDefs.BITAND[ImageDefs.CleanupMask[why], this.mask] # 0 THEN
	this.proc[why ! ANY => IF why = Abort OR why = Finish THEN CONTINUE];
      AddCleanupProcedure[this];
      this ← next;
      ENDLOOP;
    SELECT why FROM
      Finish => ImageDefs.StopMesa[];
      Abort => ImageDefs.AbortMesa[];
      ENDCASE;
    END;

  --  Signaller Overflow routines; known not to be called in resident stuff


  ReturnError: PROCEDURE [signal: SIGNAL, message: UNSPECIFIED] =
    BEGIN
    caller: FrameHandle = FrameOps.GetReturnFrame[];
    FrameOps.SetReturnLink[caller.returnlink];
    SDOps.Signal[signal, message ! UNWIND => FrameOps.Free[caller]];
    FrameOps.Free[caller];
    ERROR TrapDefs.ResumeError
    END;

  ReturnErrorList: PROCEDURE [signal: SIGNAL, message: POINTER TO UNSPECIFIED] =
    BEGIN
    caller: FrameHandle = FrameOps.GetReturnFrame[];
    FrameOps.SetReturnLink[caller.returnlink];
    SDOps.Signal[
      signal, message !
      UNWIND => BEGIN FrameOps.Free[caller]; FrameOps.Free[message]; END];
    FrameOps.Free[caller];
    FrameOps.Free[message];
    ERROR TrapDefs.ResumeError
    END;

  -- Main Body;

  SDDefs.SD[SDDefs.sGoingAway] ← UserCleanupProc;

  END...