-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- TestChecksums.mesa, HGM,  9-Jan-83 21:53:25

DIRECTORY
  Checksum USING [ComputeChecksum, ComputeChecksumSoftware, ComputeChecksumProc],
  ESCAlpha USING [aROTATE],
  FormSW USING [
    AllocateItemDescriptor, ClientItemsProcType, CommandItem, newLine, ProcType,
    StringItem],
  Heap USING [systemZone],
  Mopcodes USING [zACD, zADD, zDUP, zINC, zLI0, zLI1, zESC],
  MsgSW USING [Post],
  Process USING [Detach, Pause, Yield],
  Put USING [Line],
  Runtime USING [GetBcdTime],
  String USING [
    AppendChar, AppendLongNumber, AppendNumber, AppendString, StringToNumber],
  System USING [GetClockPulses, Microseconds, Pulses, PulsesToMicroseconds],
  Time USING [Append, Unpack],
  Tool USING [Create, MakeSWsProc, MakeMsgSW, MakeFormSW, MakeFileSW],
  ToolWindow USING [TransitionProcType],
  Window USING [Handle];

TestChecksums: PROGRAM
  IMPORTS
    Checksum, FormSW, Heap, MsgSW, Process, Put, Runtime, String, System, Time,
    Tool
  SHARES Checksum =
  BEGIN
  
  z: UNCOUNTED ZONE = Heap.systemZone;

  dataSize: CARDINAL = 1000B;
  data: ARRAY [0..dataSize) OF CARDINAL;
  msg, form, log: Window.Handle ← NIL;
  valueString: LONG STRING ← z.NEW[StringBody[6]];
  indexString: LONG STRING ← z.NEW[StringBody[6]];
  stopIndexString: LONG STRING ← z.NEW[StringBody[6]];
  running, pleaseStop: BOOLEAN ← FALSE;

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

  SameProc: FormSW.ProcType =
    BEGIN
    stop, i: CARDINAL;
    error: BOOLEAN ← FALSE;
    stop ← String.StringToNumber[
      stopIndexString, 8 !
      ANY =>
        BEGIN
        MsgSW.Post[msg, "The StopIndex is incorrectly specified; try again."L];
        error ← TRUE;
        CONTINUE;
        END];
    IF error THEN RETURN;
    FOR i IN [1..stop) DO data[i] ← data[0]; ENDLOOP;
    END;

  GeomProc: FormSW.ProcType =
    BEGIN
    power, stop, i: CARDINAL;
    error: BOOLEAN ← FALSE;
    stop ← String.StringToNumber[
      stopIndexString, 8 !
      ANY =>
        BEGIN
        MsgSW.Post[msg, "The StopIndex is incorrectly specified; try again."L];
        error ← TRUE;
        CONTINUE;
        END];
    IF error THEN RETURN;
    power ← data[1]/data[0];
    FOR i IN [2..stop) DO
      data[i] ← data[i - 1]*power; IF data[i] = 0 THEN data[i] ← data[0]; ENDLOOP;
    END;

  FibProc: FormSW.ProcType =
    BEGIN
    stop, i: CARDINAL;
    error: BOOLEAN ← FALSE;
    stop ← String.StringToNumber[
      stopIndexString, 8 !
      ANY =>
        BEGIN
        MsgSW.Post[msg, "The StopIndex is incorrectly specified; try again."L];
        error ← TRUE;
        CONTINUE;
        END];
    IF error THEN RETURN;
    FOR i IN [2..stop) DO data[i] ← data[i - 1] + data[i - 2]; ENDLOOP;
    END;

  ReplaceProc: FormSW.ProcType =
    BEGIN
    val, i: CARDINAL;
    error: BOOLEAN ← FALSE;
    i ← String.StringToNumber[
      indexString, 8 !
      ANY =>
        BEGIN
        MsgSW.Post[msg, "The Index is incorrectly specified; try again."L];
        error ← TRUE;
        CONTINUE;
        END];
    val ← String.StringToNumber[
      valueString, 8 !
      ANY =>
        BEGIN
        MsgSW.Post[msg, "The Wordvalue is incorrectly specified; try again."L];
        error ← TRUE;
        CONTINUE;
        END];
    IF i >= dataSize THEN
      BEGIN
      MsgSW.Post[msg, "The Index is out of bounds; try again."L];
      error ← TRUE;
      END;
    IF error THEN RETURN;
    data[i] ← val;
    END;

  StopProc: FormSW.ProcType =
    BEGIN pleaseStop ← TRUE; WHILE (running) DO Process.Pause[1]; ENDLOOP; END;

  StartProc: FormSW.ProcType =
    BEGIN
    stop: CARDINAL;
    error: BOOLEAN ← FALSE;
    stop ← String.StringToNumber[
      stopIndexString, 8 !
      ANY =>
        BEGIN
        MsgSW.Post[msg, "The StopIndex is incorrectly specified; try again."L];
        error ← TRUE;
        CONTINUE;
        END];
    IF running THEN
      BEGIN MsgSW.Post[msg, "A test is already running!"L]; error ← TRUE; END;
    IF error THEN RETURN;
    running ← TRUE;
    pleaseStop ← FALSE;
    Put.Line[
      log,
      "
  Ix  Value SftChk SfTim  emChk emTim   %  muChk muTim  %  exChk exTim   %"L];
    Process.Detach[FORK RunTest[stop]];
    END;

  RunTest: PROCEDURE [stop: CARDINAL] =
    BEGIN
    msg: STRING = [128];
    softCheckVal, trapCheckVal, microCheckVal, exCheckVal: CARDINAL;
    beginTime, stopTime: System.Pulses;
    softCheckTime, trapCheckTime, microCheckTime, exCheckTime:
      System.Microseconds;
    len: CARDINAL;
    where: LONG POINTER;
    FOR i: CARDINAL IN [0..stop) UNTIL pleaseStop DO
      len ← i + 1;
      where ← @data[0];
      -- Simple Software
      beginTime ← System.GetClockPulses[];
      softCheckVal ← Software[0, len, where];
      stopTime ← System.GetClockPulses[];
      softCheckTime ← System.PulsesToMicroseconds[[stopTime - beginTime]];
      -- Pseudo Software Support for Microcode
      beginTime ← System.GetClockPulses[];
      trapCheckVal ← Trap[0, len, where];
      stopTime ← System.GetClockPulses[];
      trapCheckTime ← System.PulsesToMicroseconds[[stopTime - beginTime]];
      -- Microcode
      beginTime ← System.GetClockPulses[];
      microCheckVal ← Checksum.ComputeChecksum[0, len, where];
      stopTime ← System.GetClockPulses[];
      microCheckTime ← System.PulsesToMicroseconds[[stopTime - beginTime]];
      -- Additional one for experimenting
      beginTime ← System.GetClockPulses[];
      exCheckVal ← ExComputeChecksum[0, len, where];
      stopTime ← System.GetClockPulses[];
      exCheckTime ← System.PulsesToMicroseconds[[stopTime - beginTime]];
      msg.length ← 0;
      D4[msg, i];
      O7[msg, data[i]];
      O7[msg, softCheckVal];
      LD6[msg, softCheckTime];
      O7[msg, trapCheckVal];
      LD6[msg, trapCheckTime];
      IF softCheckVal # trapCheckVal THEN String.AppendChar[msg, '*]
      ELSE String.AppendChar[msg, ' ];
      trapCheckTime ← (trapCheckTime*100)/softCheckTime;
      LD3[msg, trapCheckTime];
      O7[msg, microCheckVal];
      LD6[msg, microCheckTime];
      IF softCheckVal # microCheckVal THEN String.AppendChar[msg, '*]
      ELSE String.AppendChar[msg, ' ];
      microCheckTime ← (microCheckTime*100)/softCheckTime;
      LD2[msg, microCheckTime];
      O7[msg, exCheckVal];
      LD6[msg, exCheckTime];
      IF softCheckVal # exCheckVal THEN String.AppendChar[msg, '*]
      ELSE String.AppendChar[msg, ' ];
      exCheckTime ← (exCheckTime*100)/softCheckTime;
      LD3[msg, exCheckTime];
      Put.Line[log, msg];
      Process.Yield[];
      ENDLOOP;
    running ← FALSE;
    END;

  D4: PROCEDURE [s: STRING, n: UNSPECIFIED] =
    BEGIN
    temp: STRING = [25];
    String.AppendNumber[temp, n, 10];
    THROUGH [temp.length..4) DO String.AppendChar[s, ' ]; ENDLOOP;
    String.AppendString[s, temp];
    END;

  LD2: PROCEDURE [s: STRING, n: LONG UNSPECIFIED] =
    BEGIN
    temp: STRING = [25];
    String.AppendLongNumber[temp, n, 10];
    THROUGH [temp.length..2) DO String.AppendChar[s, ' ]; ENDLOOP;
    String.AppendString[s, temp];
    END;

  LD3: PROCEDURE [s: STRING, n: LONG UNSPECIFIED] =
    BEGIN
    temp: STRING = [25];
    String.AppendLongNumber[temp, n, 10];
    THROUGH [temp.length..3) DO String.AppendChar[s, ' ]; ENDLOOP;
    String.AppendString[s, temp];
    END;

  LD6: PROCEDURE [s: STRING, n: LONG UNSPECIFIED] =
    BEGIN
    temp: STRING = [25];
    String.AppendLongNumber[temp, n, 10];
    THROUGH [temp.length..6) DO String.AppendChar[s, ' ]; ENDLOOP;
    String.AppendString[s, temp];
    END;

  O7: PROCEDURE [s: STRING, n: UNSPECIFIED] =
    BEGIN
    temp: STRING = [25];
    String.AppendNumber[temp, n, 8];
    THROUGH [temp.length..7) DO String.AppendChar[s, ' ]; ENDLOOP;
    String.AppendString[s, temp];
    END;

  -- Put the INLINEs within a procedure to include the XFERs
  Software: PROCEDURE [cs: CARDINAL ← 0, nWords: CARDINAL, p: LONG POINTER]
    RETURNS [checksum: CARDINAL] =
    BEGIN RETURN[Checksum.ComputeChecksumSoftware[cs, nWords, p]]; END;

  Trap: PROCEDURE [cs: CARDINAL ← 0, nWords: CARDINAL, p: LONG POINTER]
    RETURNS [checksum: CARDINAL] =
    BEGIN RETURN[Checksum.ComputeChecksumProc[cs, nWords, p]]; END;

  ExComputeChecksum: PROCEDURE [cs: CARDINAL, nWords: CARDINAL, p: LONG POINTER]
    RETURNS [checksum: CARDINAL] =
    BEGIN
    Push: PROC [CARDINAL] = MACHINE CODE BEGIN END;
    Pop: PROC RETURNS [CARDINAL] = MACHINE CODE BEGIN END;
    TOS: PROC RETURNS [CARDINAL] = MACHINE CODE BEGIN Mopcodes.zDUP; END;
    IncTOS: PROC = MACHINE CODE BEGIN Mopcodes.zINC; END;
    OnesAddAndLeftRotate: PROC [
      --cs: CARDINAL,-- wd: CARDINAL]
      --RETURNS [cs: CARDINAL]--  = MACHINE CODE
      BEGIN
      Mopcodes.zLI0;
      Mopcodes.zACD;
      Mopcodes.zADD;  -- cs ← (cs ONESADD wd)
      Mopcodes.zLI1;
      Mopcodes.zESC, ESCAlpha.aROTATE;
      END;

    --TOS←-- Push[cs];  -- leave cs on the stack throughout the procedure
    THROUGH [0..nWords) DO
      --TOS←-- OnesAddAndLeftRotate[ --cs: TOS,-- wd: p↑]; p ← p + 1 ENDLOOP;
    IF TOS[] = 177777B THEN IncTOS[];
    RETURN[Pop[ --TOS-- ]];
    END;


  MakeSWs: Tool.MakeSWsProc =
    BEGIN
    msg ← Tool.MakeMsgSW[window: window, lines: 3];
    form ← Tool.MakeFormSW[window: window, formProc: MakeForm];
    log ← Tool.MakeFileSW[window: window, name: "Checksum.log$"L];
    END;

  MakeForm: FormSW.ClientItemsProcType =
    BEGIN
    i: CARDINAL;
    nParams: CARDINAL = 9;
    items ← FormSW.AllocateItemDescriptor[nParams];
    items[i ← 0] ← FormSW.CommandItem[
      tag: "Same"L, proc: SameProc, place: FormSW.newLine];
    items[i ← i + 1] ← FormSW.CommandItem[tag: "Geom"L, proc: GeomProc];
    items[i ← i + 1] ← FormSW.CommandItem[tag: "Fib"L, proc: FibProc];
    items[i ← i + 1] ← FormSW.CommandItem[tag: "START"L, proc: StartProc];
    items[i ← i + 1] ← FormSW.CommandItem[tag: "STOP"L, proc: StopProc];
    String.AppendNumber[stopIndexString, dataSize, 8];
    items[i ← i + 1] ← FormSW.StringItem[
      tag: "StopIndex(octal)"L, string: @stopIndexString, inHeap: TRUE];
    items[i ← i + 1] ← FormSW.CommandItem[
      tag: "Replace"L, proc: ReplaceProc, place: FormSW.newLine];
    String.AppendNumber[indexString, 0, 8];
    items[i ← i + 1] ← FormSW.StringItem[
      tag: "Index(octal)"L, string: @indexString, boxWidth: 100, inHeap: TRUE];
    String.AppendNumber[valueString, 1, 8];
    items[i ← i + 1] ← FormSW.StringItem[
      tag: "with Value(octal)"L, string: @valueString, inHeap: TRUE];
    data[0] ← 1;
    RETURN[items, TRUE];
    END;

  ClientTransition: ToolWindow.TransitionProcType =
    BEGIN IF new = inactive THEN msg ← form ← NIL; END;

  -- Mainline code
  Init[];  -- this gets string out of global frame
  END...