-- Copyright (C) 1983, 1985  by Xerox Corporation. All rights reserved. 
-- TestPopCorn.mesa, HGM, 25-Jun-85  3:39:05

DIRECTORY
  FormSW USING [
    AllocateItemDescriptor, ClientItemsProcType, CommandItem,
    Display, LongNumberItem, newLine, ProcType, StringItem],
  Heap USING [systemZone],
  MsgSW USING [Post],
  Process USING [Detach, Pause, SecondsToTicks, Yield],
  ProcessorFace USING [microsecondsPerHundredPulses],
  Put USING [Char, CR, Line, LongDecimal, LongNumber, Text],
  Runtime USING [GetBcdTime],
  String USING [AppendString],
  System USING [
    GetClockPulses, GetGreenwichMeanTime,
    GreenwichMeanTime, Pulses, PulsesToMicroseconds],
  Time USING [Append, Unpack],
  Tool USING [
    Create, UnusedLogName, MakeFormSW, MakeFileSW, MakeMsgSW, MakeSWsProc],
  ToolWindow USING [TransitionProcType],
  UserInput USING [UserAbort],
  UserTerminal USING [BlinkDisplay],
  Window USING [Handle],

  PopCorn USING [Error, GetClockOffset],
  PupDefs USING [
    AppendPupAddress, GetPupAddress, PupAddress, PupNameTrouble,
    PupPackageDestroy, PupPackageMake];

TestPopCorn: PROGRAM
  IMPORTS
    FormSW, Heap, MsgSW, Process, ProcessorFace, Put, Runtime, String, System,
    Time, Tool, UserInput, UserTerminal,
    PopCorn, PupDefs =
  BEGIN

  z: UNCOUNTED ZONE = Heap.systemZone;

  msg, log, form: Window.Handle ← NIL;
  target: LONG STRING ← NIL;
  lo: LONG INTEGER ← -9999999;
  hi: LONG INTEGER ← 9999999;
  min, max: LONG CARDINAL ← 1000000;
  error: LONG CARDINAL ← 0;
  running: BOOLEAN ← FALSE;

  Init: PROCEDURE =
    BEGIN
    herald: STRING = [100];
    String.AppendString[herald, "TestPopCorn of  "L];
    Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]];
    [] ← Tool.Create[
      name: herald, makeSWsProc: MakeSWs, clientTransition: Transition];
    END;

  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    logFileName: STRING = [40];
    msg ← Tool.MakeMsgSW[window: window, lines: 1];
    form ← Tool.MakeFormSW[window: window, formProc: MakeItemArray];
    Tool.UnusedLogName[logFileName, "TestPopCorn.log$"L];
    log ← Tool.MakeFileSW[window: window, name: logFileName];
    END;

  MakeItemArray: FormSW.ClientItemsProcType =
    BEGIN
    nItems: CARDINAL = 9;
    i: INTEGER ← -1;
    items ← FormSW.AllocateItemDescriptor[nItems];
    items[i ← i + 1] ← FormSW.CommandItem[
      tag: "PokeIt"L, place: FormSW.newLine, proc: PokeIt];
    items[i ← i + 1] ← FormSW.CommandItem[tag: "Tick"L, proc: Tick];
    items[i ← i + 1] ← FormSW.CommandItem[tag: "Tock"L, proc: Tock];
    items[i ← i + 1] ← FormSW.StringItem[tag: "Target"L, string: @target, inHeap: TRUE];
    items[i ← i + 1] ← FormSW.LongNumberItem[tag: " Lo"L, value: @lo, place: FormSW.newLine];
    items[i ← i + 1] ← FormSW.LongNumberItem[tag: " Hi"L, value: @hi];
    items[i ← i + 1] ← FormSW.LongNumberItem[tag: "Min"L, value: @min, place: FormSW.newLine];
    items[i ← i + 1] ← FormSW.LongNumberItem[tag: "Max"L, value: @max];
    items[i ← i + 1] ← FormSW.LongNumberItem[tag: "Error"L, value: @error];
    IF (i + 1) # nItems THEN ERROR;
    RETURN[items, TRUE];
    END;

  Transition: ToolWindow.TransitionProcType =
    BEGIN
    SELECT TRUE FROM
      old = inactive =>  -- tool is becomming active
        BEGIN
        [] ← PupDefs.PupPackageMake[];
        target ← z.NEW[StringBody[40]];
        String.AppendString[target, "PopCorn"L];
        END;
      new = inactive =>
        BEGIN
        z.FREE[@target];
        PupDefs.PupPackageDestroy[];
        END;
      ENDCASE;
    END;

  PokeIt: FormSW.ProcType =
    BEGIN
    where: PupDefs.PupAddress;
    IF running THEN
      BEGIN
      MsgSW.Post[msg, "Already running."L];
      RETURN;
      END;
    Header["Poking...."L];
    Put.Text[log, target];
    Put.Char[log, '=];
    PupDefs.GetPupAddress[@where, target !
      PupDefs.PupNameTrouble =>
        BEGIN MsgSW.Post[msg, e]; Put.Line[log, e]; GOTO Trouble; END];
    PrintPupAddress[where];
    Put.Line[log, "."L];
    running ← TRUE;
    Process.Detach[FORK Poker[where]];
    EXITS Trouble => NULL;
    END;
    
  Poker: PROCEDURE [where: PupDefs.PupAddress] =
    BEGIN
    UNTIL UserInput.UserAbort[log] DO
      diff: LONG INTEGER;
      flight, error: LONG CARDINAL;
      [diff, flight] ← PopCorn.GetClockOffset[where, 2 !
        PopCorn.Error =>
          BEGIN
	  Put.Text[log, "  Error from PupCorn: "L];
	  Put.Text[log, text];
	  Put.Line[log, "."L];
	  GOTO Trouble;
	  END;];
      Put.Text[log, target];
      Put.Text[log, " says it's clock is "L];
      Put.LongDecimal[log, diff];
      Put.Text[log, " ms faster than ours.  The flight time was "L];
      Put.LongDecimal[log, flight];
      Put.Line[log, " ms."L];
      error ← flight + 80;  -- DLion clock jitters  ****************
      CheckLoHigh[diff - error, diff + error];
      Process.Pause[Process.SecondsToTicks[5]];
      ENDLOOP;
    Trailer["Finished."L];
    running ← FALSE;
    EXITS Trouble => running ← FALSE;
    END;
  
  CheckLoHigh: PROCEDURE [newLow, newHigh: LONG INTEGER] =
    BEGIN
    hit: BOOLEAN ← FALSE;
    IF newLow > lo THEN
      BEGIN
      hit ← TRUE;
      lo ← newLow;
      Put.Text[log, "Better Lo is "L];
      Put.LongDecimal[log, lo];
      Put.Line[log, "."L];
      END;
    IF newHigh < hi THEN
      BEGIN
      hit ← TRUE;
      hi ← newHigh;
      Put.Text[log, "Better Hi is "L];
      Put.LongDecimal[log, hi];
      Put.Line[log, "."L];
      END;
    IF hit THEN
      BEGIN
      FormSW.Display[form];
      Put.Text[log, "Time is now known within "L];
      Put.LongDecimal[log, (hi - lo + 1) / 2];
      Put.Line[log, " ms."L];
      END;
    IF lo > hi THEN
      BEGIN
      UserTerminal.BlinkDisplay[];
      Put.Line[log, " ******** Clocks are mixed up."L];
      lo ← -999999;
      hi ← 999999;
      FormSW.Display[form];
      END;
    END;

  Tick: FormSW.ProcType =
    BEGIN
    IF running THEN
      BEGIN
      MsgSW.Post[msg, "Already running."L];
      RETURN;
      END;
    Header["Ticking... (using processor clock)."L];
    running ← TRUE;
    Process.Detach[FORK Ticker[]];
    END;
    
  Ticker: PROCEDURE =
    BEGIN
    FOR i: CARDINAL IN [0..100) UNTIL UserInput.UserAbort[log] DO
      start, stop, oldStart, oldStop: System.Pulses;
      first, second: System.GreenwichMeanTime;
      start ← System.GetClockPulses[];
      first ← System.GetGreenwichMeanTime[];
      DO
        temp: System.Pulses ← System.GetClockPulses[];
	second ← System.GetGreenwichMeanTime[];
        stop ← System.GetClockPulses[];
        IF first # second THEN EXIT;
	start ← temp;
        ENDLOOP;
      IF i # 0 THEN
        BEGIN
        slop: LONG CARDINAL;
        slop ← System.PulsesToMicroseconds[[stop-start]];
        Put.LongNumber[log, stop-start, [10, FALSE, FALSE, 4]];
        Put.LongNumber[log, slop, [10, FALSE, FALSE, 6]];
        Put.LongNumber[log, System.PulsesToMicroseconds[[start-oldStart]], [10, FALSE, FALSE, 8]];
	Put.Line[log, "."L];
	CheckMinMax[[stop - oldStart], [start - oldStop]];
	END;
      THROUGH [0..10) DO Process.Yield[]; ENDLOOP;
      oldStart ← start;
      oldStop ← stop;
      ENDLOOP;
    Trailer["Finished."L];
    running ← FALSE;
    END;
	
  Tock: FormSW.ProcType =
    BEGIN
    IF running THEN
      BEGIN
      MsgSW.Post[msg, "Already running."L];
      RETURN;
      END;
    Header["Tocking... (using our clock)."L];
    running ← TRUE;
    Process.Detach[FORK Tocker[]];
    END;
    
  Tocker: PROCEDURE =
    BEGIN
    FOR i: CARDINAL IN [0..100) UNTIL UserInput.UserAbort[log] DO
      start, stop, oldStart, oldStop: System.Pulses;
      first, second: System.GreenwichMeanTime;
      start ← System.GetClockPulses[];
      first ← MyGetGreenwichMeanTime[];
      DO
        temp: System.Pulses ← System.GetClockPulses[];
	second ← MyGetGreenwichMeanTime[];
        stop ← System.GetClockPulses[];
        IF first # second THEN EXIT;
	start ← temp;
        ENDLOOP;
      IF i # 0 THEN
        BEGIN
        slop: LONG CARDINAL;
        slop ← System.PulsesToMicroseconds[[stop-start]];
        Put.LongNumber[log, stop-start, [10, FALSE, FALSE, 4]];
        Put.LongNumber[log, slop, [10, FALSE, FALSE, 6]];
        Put.LongNumber[log, System.PulsesToMicroseconds[[start-oldStart]], [10, FALSE, FALSE, 8]];
	Put.Line[log, "."L];
	CheckMinMax[[stop - oldStart], [start - oldStop]];
	END;
      THROUGH [0..10) DO Process.Yield[]; ENDLOOP;
      oldStart ← start;
      oldStop ← stop;
      ENDLOOP;
    Trailer["Finished."L];
    running ← FALSE;
    END;
	
  CheckMinMax: PROCEDURE [minPulses, maxPulses: System.Pulses] =
    BEGIN
    hit: BOOLEAN ← FALSE;
    newMin: LONG CARDINAL ← System.PulsesToMicroseconds[minPulses];
    newMax: LONG CARDINAL ← System.PulsesToMicroseconds[maxPulses];
    UNTIL newMax < 1500000 DO newMax ← newMax - 1000000; ENDLOOP;
    IF newMin < min THEN
      BEGIN
      hit ← TRUE;
      min ← newMin;
      Put.Text[log, "New Min is "L];
      Put.LongDecimal[log, min];
      Put.Line[log, "."L];
      END;
    IF newMax > max THEN
      BEGIN
      hit ← TRUE;
      max ← newMax;
      Put.Text[log, "New Max is "L];
      Put.LongDecimal[log, max];
      Put.Line[log, "."L];
      END;
    IF hit THEN
      BEGIN
      oldError: LONG CARDINAL ← error;
      error ← MAX[(1000000 - min), (max - 1000000)];
      FormSW.Display[form];
      IF error # oldError THEN
        BEGIN
        Put.Text[log, "Clock error is now up to "L];
        Put.LongDecimal[log, error];
        Put.Line[log, " microsec."L];
	END;
      END;
    END;
      
  -- Simulate greenwich mean time features of ProcessorFace using
  -- interval timer because DLion TOD clock has much jitter

  MyGetGreenwichMeanTime: PROCEDURE RETURNS [System.GreenwichMeanTime] =
    BEGIN
    seconds: System.GreenwichMeanTime =
      [((System.GetClockPulses[] - pulsesGmtSimulated)*100)/pulsesPer100Seconds];
    pulsesGmtSimulated ← pulsesGmtSimulated + seconds*pulsesPer100Seconds/100;
    -- long multiply!  The truncation keeps pulsesGmtSimulated at the right value
    gmtSimulated ← [gmtSimulated + seconds];
    RETURN[gmtSimulated]
    END;
      
  MySetGreenwichMeanTime: PROCEDURE [gmt: System.GreenwichMeanTime] =
    BEGIN
    pulsesGmtSimulated ← System.GetClockPulses[];
    gmtSimulated ← gmt;
    END;
  
  gmtSimulated: System.GreenwichMeanTime;
  pulsesGmtSimulated: LONG CARDINAL;
  pulsesPer100Seconds: LONG CARDINAL ← 10*(1D9/ProcessorFace.microsecondsPerHundredPulses);
  
  Header: PROCEDURE [s: STRING] =
    BEGIN
    Put.CR[log];
    Put.Line[log, s];
    MsgSW.Post[msg, s];
    END;
  
  Trailer: PROCEDURE [s: STRING] =
    BEGIN
    Put.Line[log, s];
    MsgSW.Post[msg, s];
    END;
    
  PrintPupAddress: PROCEDURE [a: PupDefs.PupAddress] =
    BEGIN
    temp: STRING = [40];
    PupDefs.AppendPupAddress[temp, a];
    Put.Text[log, temp];
    END;

  MySetGreenwichMeanTime[System.GetGreenwichMeanTime[]];
  Init[];
  END...