-- SystemImpl.mesa (last edited by: Levin on: August 26, 1982 9:09 am)
-- THINGS TO DO:
-- 1) Get rid of call to TemporarySetGMT.SetGMT if ever through with gmt clock simulation

DIRECTORY
  ControlPrograms USING [],
  DeviceCleanup USING [Await, Item, Reason],
  Environment USING [Long],
  Inline USING [LongDiv, LongDivMod, LongMult],
  KernelPhysicalVolume USING [
    GetSavedLocalTimeParameters, SetSavedLocalTimeParameters],
  Process USING [MsecToTicks, SetTimeout],
  ProcessorFace USING [
    GetGreenwichMeanTime, gmtEpoch, microsecondsPerHundredPulses, PowerOff,
    processorID, ResetAutomaticPowerOn, SetAutomaticPowerOn],
  RuntimeInternal USING [WorryCallDebugger],
  SpecialSystem USING [ProcessorID],
  System USING [GetClockPulses, GreenwichMeanTime, LocalTimeParameters,
    Microseconds, PhysicalVolumeID, Pulses, TimerHandle, UniversalID],
  SystemInternal USING [UniversalID],
  TemporarySetGMT USING [SetGMT],
  Volume USING [Close, GetNext, ID, nullID];

SystemImpl: MONITOR
  IMPORTS
    DeviceCleanup, KernelPhysicalVolume, Inline, Process, ProcessorFace,
    RuntimeInternal, System, TemporarySetGMT, Volume
  EXPORTS SpecialSystem, System, SystemInternal, ControlPrograms =
  BEGIN OPEN System;

    -- Interval timers

  GetIntervalTime: PUBLIC SAFE PROC [t: TimerHandle] RETURNS [Microseconds] = TRUSTED {
    RETURN[PulsesToMicroseconds[[GetClockPulses[] - LOOPHOLE[t, Pulses]]]]};

  PulsesToMicroseconds: PUBLIC SAFE PROC [p: Pulses] RETURNS [Microseconds] = TRUSTED
    BEGIN -- (p*msPerHp)/(100units/hundred) 
    RETURN[MultThenDiv[p, ProcessorFace.microsecondsPerHundredPulses, 100]]
    END;

  MicrosecondsToPulses: PUBLIC SAFE PROC [m: Microseconds] RETURNS [Pulses] = TRUSTED
    BEGIN -- (microseconds*100units/hundred)/microsecondsPerHundredPulses
    RETURN[[MultThenDiv[m, 100, ProcessorFace.microsecondsPerHundredPulses]]]
    END;

  MultThenDiv: PROC [m1: LONG CARDINAL, m2: CARDINAL, dv: CARDINAL]
    RETURNS [result: LONG CARDINAL] =
    BEGIN OPEN Inline, mm1: LOOPHOLE[m1, num Environment.Long];
    t: MACHINE DEPENDENT RECORD [
      SELECT OVERLAID * FROM
	separate => [low, mid, high: CARDINAL],
	lower => [lowlong: LONG CARDINAL, junk: CARDINAL],
	higher => [junk: CARDINAL, highlong: LONG CARDINAL],
	ENDCASE];
    t.lowlong ← LongMult[mm1.lowbits, m2];
    IF mm1.highbits # 0 THEN
      BEGIN
      t.highlong ← LongMult[mm1.highbits, m2] + t.mid;
      IF t.high # 0 THEN
	BEGIN OPEN q: LOOPHOLE[result, num Environment.Long];
	-- have to do triple divide
	IF t.high >= dv THEN t.high ← t.high MOD dv; -- overflow; lowbits will be right
	[quotient: q.highbits, remainder: t.mid] ← LongDivMod[t.highlong, dv];
	q.lowbits ← LongDiv[t.lowlong, dv];
	RETURN;
	END;
      END;
    -- t.high is 0, so let mesa do the work...
    RETURN[t.lowlong/LONG[dv]];
    END;

  -- GMT

  -- This is an entry procedure to serialize access to ProcessorFace.GetGreenwichMeanTime
  GetGreenwichMeanTime: PUBLIC ENTRY SAFE PROC RETURNS [GreenwichMeanTime] =
    TRUSTED {RETURN[LOOPHOLE[ProcessorFace.GetGreenwichMeanTime[]]]};

ltpState: {
  unknown, -- => ltp undefined
  known, -- => ltp defined, but may not be saved on nonvolatile storage
  knownAndSaved} -- ltp defined and saved on nonvolatile storage
 ← unknown;
ltp: LocalTimeParameters;

InitializeLocalTimeParameters: PROC =
  BEGIN
  valid: BOOLEAN;
  [valid, ltp] ← TemporarySetGMT.SetGMT[];
  IF valid THEN ltpState ← known
  END;

GetLocalTimeParameters: PUBLIC ENTRY SAFE PROC [pvID: PhysicalVolumeID]
    RETURNS [LocalTimeParameters] = TRUSTED
  BEGIN
  valid: BOOLEAN;
  SELECT ltpState FROM
    unknown => -- try to read them from nonvolatile storage
      BEGIN
      [valid, ltp] ← KernelPhysicalVolume.GetSavedLocalTimeParameters[pvID];
      IF valid THEN ltpState ← knownAndSaved
      ELSE RETURN WITH ERROR LocalTimeParametersUnknown;
      END;
    known => --  (try to) make sure they are saved on nonvolatile storage
      SetSavedLTP[pvID];
    ENDCASE;
  RETURN[ltp]
  END;

LocalTimeParametersUnknown: PUBLIC ERROR = CODE;

SetLocalTimeParameters: PUBLIC ENTRY PROC [
    params: LocalTimeParameters, pvID: PhysicalVolumeID] =
  BEGIN
  SELECT TRUE FROM
    ltpState=knownAndSaved AND ltp=params => NULL;
    ENDCASE => {ltp ← params; ltpState ← known; SetSavedLTP[pvID]};
  END;

-- Write local time parameters to nonvolatile storage, if they are different.
SetSavedLTP: INTERNAL PROC [pvID: PhysicalVolumeID] =
  BEGIN OPEN KernelPhysicalVolume;
  valid: BOOLEAN; savedltp: LocalTimeParameters;
  [valid, savedltp] ← GetSavedLocalTimeParameters[pvID];
  IF ~valid OR savedltp~=ltp THEN
    IF NOT SetSavedLocalTimeParameters[params: ltp, pvID: pvID].updated
      THEN GOTO NotSaved;
  ltpState ← knownAndSaved; -- (unless pvID=nullID and IsUtilityPilot[])
  EXITS NotSaved => NULL;
  END;

  -- Processor identification (SpecialSystem)

  GetProcessorID: PUBLIC PROC RETURNS [SpecialSystem.ProcessorID] = {
    RETURN[LOOPHOLE[ProcessorFace.processorID]]};

  -- Universal identifiers

  UniversalID: PUBLIC TYPE = SystemInternal.UniversalID;

  -- Generate new universalID from the processorID and universal id counter.
  -- Sequence field of resultant value always less than
  --  SecondsSinceEpoch[GetGreenwichMeanTime].

  GetUniversalID: PUBLIC ENTRY PROC RETURNS [UniversalID] =
    BEGIN
    secondsSinceEpoch: LONG CARDINAL;
    nextUID: SystemInternal.UniversalID;
    -- If clock isn't set, GetGreenwichMeanTime returns gmtEpoch, so uidCounter=0
    DO
      secondsSinceEpoch ← ProcessorFace.GetGreenwichMeanTime[] - ProcessorFace.gmtEpoch;
      IF secondsSinceEpoch = 0 THEN
	RuntimeInternal.WorryCallDebugger["GMT clock not set: GetUniversalID"L];
      IF ~uidCounterValid THEN
	BEGIN
	uidCounter ← secondsSinceEpoch; -- clock set after initialization
	uidCounterValid ← TRUE;
	END;
      IF uidCounter < secondsSinceEpoch THEN EXIT;
      WAIT oneSecond;
      ENDLOOP;
    nextUID ← [processor: LOOPHOLE[ProcessorFace.processorID], sequence: uidCounter];
    uidCounter ← uidCounter + 1;
    RETURN[nextUID]
    END;

  oneSecond: CONDITION;
  uidCounter: LONG CARDINAL; -- always <=SecondsSinceEpoch[GetGreenwichMeanTime[]]
  uidCounterValid: BOOLEAN ← FALSE;

  InitializeUIDCleanup: PROC =
    BEGIN OPEN DeviceCleanup;
    item: Item;
    reason: Reason;
    DO
      reason ← Await[@item];
      SELECT reason FROM turnOff => uidCounterValid ← FALSE; ENDCASE;
      ENDLOOP;
    END;

  -- System Power Control

  PowerOff: PUBLIC PROC =
    BEGIN
    vID: Volume.ID ← Volume.nullID;
    UNTIL (vID ← Volume.GetNext[vID]) = Volume.nullID DO
      Volume.Close[vID] ENDLOOP;
    ProcessorFace.PowerOff[];
    END;

  SetAutomaticPowerOn: PUBLIC SAFE PROC [
    time: GreenwichMeanTime, externalEvent: BOOLEAN] = TRUSTED {
    ProcessorFace.SetAutomaticPowerOn[time, externalEvent]; };

  ResetAutomaticPowerOn: PUBLIC SAFE PROC = TRUSTED
    {ProcessorFace.ResetAutomaticPowerOn[]; };

  -- SystemInternal

  Unimplemented: PUBLIC SIGNAL = CODE;

  -- Initialization

  InitializeLocalTimeParameters[]; -- may set GMT from Ethernet too
  Process.SetTimeout[@oneSecond, Process.MsecToTicks[1000]];
    -- set timeout to (approx) one second
  InitializeUIDCleanup[];
  END.

(For earlier log entries see Pilot 3.0 archive version.)
February 4, 1980  3:56 PM	McJones	Move gmt-keeping below OISProcessorFace
February 26, 1980  12:22 PM	McJones	AR1840: Reset uidCounter on return from debugger
April 15, 1980  3:11 PM	McJones	Move InitializeGMT to another module
June 24, 1980  4:39 PM	McJones	Convert to 48-bit processor ids and 80-bit universal ids; OISProcessorFace=>ProcessorFace
October 3, 1980  7:56 PM	Forrest	Convert to use 48 bit arithmetic in usec<=>pulses
January 23, 1981  2:50 PM	McJones	Local time parameters
February 4, 1981  3:09 PM	McJones	SystemExtras=>System
February 14, 1981  6:34 PM	McJones	Set ltpState at least to known in SetLocalTimeParameters
August 26, 1982 9:09 am	Levin	Make things SAFE.