-- Copyright (C) 1984  by Xerox Corporation. All rights reserved. 
-- Alive.mesa, HGM, 19-Apr-84 17:40:46
-- Do something so I can see it's still alive...
-- This is also the Shift-STOP watcher.  (Only it's the Alt-Boot button.)

-- Hacked to bypass PROMs that defer wakeups

DIRECTORY
  DicentraInputOutput USING [GetExternalStatus, SetExtCtrl, SetWakeupBits],
  Inline USING [LongDiv, LongMult, LowHalf],
  PilotClient USING [],
  Process USING [Milliseconds, MsecToTicks, Pause, SetPriority, Ticks, Yield],
  ProcessPriorities USING [priorityClientLow, priorityFrameFault, priorityPageFaultLow],
  Runtime USING [CallDebugger],
  System USING [GetClockPulses, MicrosecondsToPulses, Pulses, PulsesToMicroseconds],
  
  CpuIdle USING [],
  Watchdog USING [Deactivate];

UtilityPilotClientImpl: PROGRAM
  IMPORTS DicentraInputOutput, Inline, Process, Runtime, System, Watchdog
  EXPORTS PilotClient, CpuIdle =
  BEGIN
  
  displayUpdateMsec: Process.Milliseconds = 1000;
  cyclesPerSecond: LONG CARDINAL ← 0;
  pulsesPerSecond: LONG CARDINAL;
  cycles: LONG CARDINAL ← 0;
  used: CARDINAL ← 0;

  watcher: PROCESS = FORK Watcher[];
  blinker: PROCESS;
  background: PROCESS;
  
  GetSmoothedCpuUtilization: PUBLIC PROCEDURE RETURNS [CARDINAL] =
    BEGIN
    RETURN[used/10];
    END;

  GetCycles: PUBLIC PROCEDURE RETURNS[LONG CARDINAL] =
    BEGIN
    RETURN[cycles];
    END;

  GetCalibration: PUBLIC PROCEDURE RETURNS [LONG CARDINAL, LONG CARDINAL, BOOLEAN] =
    BEGIN
    RETURN[cyclesPerSecond, pulsesPerSecond, TRUE];
    END;

  Run: PUBLIC PROCEDURE =
    BEGIN  -- Start Blinker after Start Traps have done the real work
    blinker ← FORK Blinker[];
    background ← FORK Background[];
    END;
  
  out: WORD ← 0;
  
  Watcher: PROCEDURE =
    BEGIN
    ticks: Process.Ticks = Process.MsecToTicks[displayUpdateMsec];
    points: ARRAY [0..4) OF WORD ← [87H, 47H, 27H, 17H];
    down: CARDINAL ← 0;
    Process.SetPriority[ProcessPriorities.priorityFrameFault];
    IF DicentraInputOutput.GetExternalStatus[].altBoot THEN
      BEGIN
      Watchdog.Deactivate[];
      Runtime.CallDebugger["Key Stop 2.001"L];
      END;
    DO
      FOR i: CARDINAL IN [0..4) DO
        out ← points[i];
	IF DicentraInputOutput.GetExternalStatus[].altBoot THEN
	  BEGIN
	  down ← down + 1;
	  IF down = 2 THEN
	    BEGIN
	    Watchdog.Deactivate[];
	    Runtime.CallDebugger["Poof"L];
	    END
	  ELSE out ← 0F7H;  -- All 4
	  END
	ELSE down ← 0;
        DicentraInputOutput.SetExtCtrl[out];
        Process.Pause[ticks];
        ENDLOOP;
      ENDLOOP;
    END;
  
  Blinker: PROCEDURE =
    BEGIN
    ticks: Process.Ticks = Process.MsecToTicks[displayUpdateMsec];
    Process.SetPriority[ProcessPriorities.priorityPageFaultLow];
    Calibrate[]; 
    DO
      Process.Pause[ticks];
      ShowIdleTime[];
      DicentraInputOutput.SetExtCtrl[out];
      ENDLOOP;
    END;
  
  Background: PROCEDURE =
    BEGIN
    Process.SetPriority[ProcessPriorities.priorityClientLow];
    DO
      THROUGH [0..50000) DO
        cycles ← SUCC[cycles];
	DicentraInputOutput.SetWakeupBits[0];  -- Hack for buggy PROMs
        Process.Yield[];
	ENDLOOP;
      ENDLOOP;
    END;
  
  startPulses: LONG CARDINAL ← System.GetClockPulses[]; 
  startCycles: LONG CARDINAL ← cycles;
  ShowIdleTime: PROCEDURE =
    BEGIN
    pulsesThisRefresh: LONG CARDINAL ← System.GetClockPulses[] - startPulses;
    cyclesThisRefresh: LONG CARDINAL ← cycles - startCycles;
    cyclesThisTry: LONG CARDINAL ← cyclesThisRefresh * pulsesPerSecond / pulsesThisRefresh;
    idle: CARDINAL ← Inline.LowHalf[cyclesThisTry * 1000 / cyclesPerSecond];
    IF idle > 1000 THEN idle ← 1000;
    used ← Inline.LongDiv[1000 - idle + Inline.LongMult[used,9],10];
    SetMP[used/10];
    startPulses ← startPulses + pulsesThisRefresh; 
    startCycles ← startCycles + cyclesThisRefresh;
    END;
  
  SetMP: PROCEDURE [n: CARDINAL] =
    BEGIN  -- Hack to avoid hogging CPU
    DicentraInputOutput.SetExtCtrl[5];
    FOR i: CARDINAL IN [0..n) DO
      DicentraInputOutput.SetExtCtrl[3];
      DicentraInputOutput.SetExtCtrl[7];
      ENDLOOP;
    END;

  Calibrate: PROCEDURE =
    BEGIN
    cyclesToSample: CARDINAL = 10000;  -- about a second on a DLion
    first, last: System.Pulses;
    nanoSecPerCycle: LONG CARDINAL;
    first ← System.GetClockPulses[];
    THROUGH [0..cyclesToSample) DO
      cycles ← SUCC[cycles];
      DicentraInputOutput.SetWakeupBits[0];  -- Hack for buggy PROMs
      Process.Yield[];
      ENDLOOP;
    -- This appears to go slow if an extra timeout scan happens, packet arrives or ...
    -- Unfortunately, that causes confusion when idle > 100%
    last ← System.GetClockPulses[];
    cycles ← cycles - cyclesToSample;
    nanoSecPerCycle ← System.PulsesToMicroseconds[[LONG[1000]*(last-first)]]/cyclesToSample;
    cyclesPerSecond ← (1000000*displayUpdateMsec)/nanoSecPerCycle;
    pulsesPerSecond ← System.MicrosecondsToPulses[displayUpdateMsec*LONG[1000]];
    END;

  END.....