-- File: IntimeImpl.mesa
-- Last edited by
--   Dan Swinehart March 23, 1982 12:58 pm
--   MBrown on 23-Mar-82 16:03:41
--   Dan Swinehart on April 14, 1982 11:04 am

DIRECTORY
  Intime,
  Inline USING [ LowHalf, LongDiv, LongNumber, HighHalf ],
  Process USING [ SecondsToTicks, TicksToMsec ],
  SpecialSpace USING [ MakeGlobalFrameResident, MakeCodeResident ],
  System USING [ GetGreenwichMeanTime, GetClockPulses, MicrosecondsToPulses,
    Pulses, PulsesToMicroseconds, SecondsSinceEpoch ]
  ;

IntimeImpl: MONITOR
  IMPORTS Inline, Process, SpecialSpace, System 
  EXPORTS Intime
  SHARES Intime =
  BEGIN OPEN Intime;

  msPerDeltaTick: PUBLIC MsTicks ← Process.TicksToMsec[1];
  deltaTicksPerSecond: PUBLIC DeltaTicks ← Process.SecondsToTicks[1];

  pulsesPerSecond: System.Pulses = System.MicrosecondsToPulses[m: 1000000];
  adjustInterval: System.Pulses = [pulsesPerSecond-1];
  impossibleInterval: LONG CARDINAL = 30; -- seconds, must be < 64K/1000

  seconds: LONG CARDINAL;
    --seconds since System.gmtEpoch the last time we set milliseconds
  milliseconds: EventTime;
    -- milliseconds since System.gmtEpoch
  fastBase: System.Pulses;
    -- value of System.GetClockPulses[] at time (in the recent past) 
    --when milliseconds = System.GetGreenwichMeanTime[].
    -- things stop working if System.GetClockPulses[]-fastBase gets large (one minute or so).
    -- Frequently-running process is supposed to call AdjustEventTime often enough to
    --  update fastBase within adjustInterval pulses.

  ReadEventTime: PUBLIC ENTRY PROC [] RETURNS [res: EventTime] = {
    now: System.Pulses; init: BOOLEAN←FALSE;
    WHILE (now ← [System.GetClockPulses[]-fastBase])>adjustInterval DO
      InternalAdjustEventTime[init]; init←TRUE; ENDLOOP; -- repeats at most once.
    RETURN[Increment[by: Inline.LongDiv[num: System.PulsesToMicroseconds[now], den: 1000]]] };

  Increment: INTERNAL PROC [by: --milliseconds--CARDINAL]
      RETURNS [res: EventTime] = INLINE {
      res← [hiShort[higher: 0, lower: LONG[by] + LONG[milliseconds.lo]]];
      res.hi ← res.hi + milliseconds.hi };

  AdjustEventTime: PUBLIC ENTRY PROC[initialize: BOOLEAN] = {
      InternalAdjustEventTime[initialize]; };

  InternalAdjustEventTime: INTERNAL PROC[initialize: BOOLEAN] = INLINE {
    newSeconds: LONG CARDINAL =
      System.SecondsSinceEpoch[System.GetGreenwichMeanTime[]];
    IF newSeconds<seconds OR newSeconds-seconds>impossibleInterval THEN initialize←TRUE;
    IF initialize THEN {
      milliseconds.higher ← 0;
      milliseconds.lower ← LONG[Inline.LowHalf[newSeconds]]*1000;
      milliseconds.hi ← milliseconds.hi + LONG[Inline.HighHalf[newSeconds]]*1000;
      fastBase ← System.GetClockPulses[]; }
    ELSE {
      dif: NAT←newSeconds-seconds;
      milliseconds ← Increment[by: 1000*dif];
      -- Usually go through following loop zero or one times,
	  --  never more than impossibleInterval times.
      THROUGH [1..dif] DO  fastBase ← [fastBase+pulsesPerSecond]  ENDLOOP };
    seconds ← newSeconds; };

  BigDifference: PUBLIC PROC [t1, t2: LONG POINTER TO READONLY EventTime]
    RETURNS [MsTicks] = {
    -- t1 presumed later than or equal to t2.  For time differences beyond the MsTicks range,
    --  returns LAST[MsTicks] --
    tt: Inline.LongNumber ← [lc[t1.lower - t2.lower]];
    RETURN[
      IF t1.higher > t2.higher + 1 OR tt.highbits # 0 THEN LAST[CARDINAL]
      ELSE tt.lowbits] };

-- Initialization --

-- Should be called BEFORE Inscript registers in!
SpecialSpace.MakeGlobalFrameResident[IntimeImpl];
SpecialSpace.MakeCodeResident[IntimeImpl];

END.