-- LupineTimeTestDriver.mesa.
-- Copyright (C) 1985 by Xerox Corporation. All rights reserved.
-- Last edited by BZM on 2-Mar-82 21:12:38.
-- Last edited by Andrew Birrell on January 26, 1983 6:08 pm
-- Bob Hagmann February 8, 1985 5:11:04 pm PST

-- This program is derived from Liaison's Mesa6 ParamTestDriver.


DIRECTORY
Heap USING [systemZone],
LupineTimeTest, --USING [ALL]
String USING [AppendLongNumber, AppendString, InvalidNumber, StringToDecimal],
Mopcodes USING [zMISC],
Process USING [Detach, Yield],
Runtime USING [CallDebugger],
SpyClient USING [StartSpy, StopSpy],
System USING [GetClockPulses, Microseconds, Pulses, PulsesToMicroseconds],
Time USING [Append, Current, Unpack],
TTY USING [
 Create, Destroy, GetLine, Handle, LineOverflow,
 NumberFormat, PutChar, PutCR, PutDecimal, PutLine,
 PutLongDecimal, PutLongNumber, PutString, Rubout,
 ResetUserAbort, UserAbort ];


LupineTimeTestDriver: PROGRAM
IMPORTS
 TimeTest: LupineTimeTest, Heap, Process, Runtime,
 SpyClient, ShortString: String, System, Time, TTY
= BEGIN


-- Compiletime parameters:

StandardNumberOfTrials: INTEGER = 1;
StandardIterationsPerTrial: INTEGER = 3000;


-- Runtime parameters:

TestParameters: TYPE = MACHINE DEPENDENT RECORD [
useDoradoClock (0): BOOLEAN ← FALSE,
countOnlyEmulatorCycles (1): BOOLEAN ← FALSE,
checkResults (2): BOOLEAN ← TRUE,
trials (3): INTEGER ← StandardNumberOfTrials,
iterationsPerTrial (4): INTEGER ← StandardIterationsPerTrial,
spying (5:0..0): BOOLEAN ← FALSE,
spyOnProcs (5:1..1): BOOLEAN ← FALSE,
showSpyData (5:2..2): {afterEachTest, afterAllTests} ← afterAllTests,
filler (5:3..15): BOOLEAN ← NULL ];

DefaultTestParameters: TestParameters = [];


-- Runtime inconsistency found:

ParamsDisagree: SIGNAL = CODE; -- An echo test failed.


-- This top-level driver routine is run in a separate process.

user: TTY.Handle;
String: TYPE = STRING;

TestProcess: PROCEDURE =
BEGIN
logFileString: STRING = [100];
logFile: String ← UniqueName[
root: "LupineTimeTest"L, suffix: ".log"L,
nameString: logFileString ];
user ← TTY.Create[name: logFile];
TestRoutine[logFile: logFile ! ABORTED => CONTINUE ];
TTY.Destroy[user];
END;

UniqueName: PROCEDURE [root, suffix, nameString: String]
RETURNS [rootSuffix: String] =
BEGIN
rootSuffix ← nameString;
rootSuffix.length ← 0;
ShortString.AppendString[to: rootSuffix, from: root];
ShortString.AppendLongNumber [
s: rootSuffix, n: System.GetClockPulses[], radix: 10 ];
ShortString.AppendString[to: rootSuffix, from: suffix];
END;


-- For periodically causing glitches (eg, NIL) that Lupine claims to handle.

Periodically: PROC [testNumber: INTEGER] RETURNS [--yes:-- BOOLEAN] =
--INLINE-- {RETURN[ (testNumber MOD 31) = 0 ]};


-- Main Timing Test.

tp: TestParameters; -- Global values.

TestRoutine: PROCEDURE[logFile: String] =
BEGIN -- The entire test is in a loop.

InitPrecisionTimings;

DO ENABLE BEGIN
 AbortTest => CONTINUE;
 UNWIND => FinishPrecisionTimings;
 END;

-- Get Test Parameters.

tp ← DefaultTestParameters;

TTY.PutCR[user];
TTY.PutLine[user, "Lupine RPC Timing Test of 24 February 1982."L];
TTY.PutString[user, "Results appear in file "L];
TTY.PutString[user, logFile]; TimeStamp[" of "L];
TTY.PutCR[user];
TTY.PutString[user, "Your options are:
D Don't check results of remote calls for correctness.
E Exclude nonemulator cycles in timings (Dorados only).
G Go (type this last).
H High precision timings (Dorados only).
P Procedure-level spying (via Copilot).
R Reset test parameters to default values.
S Spy after all tests are over.
T Spy at the conclusion of each test.
Q Quit this program immediately.
-- This line is a comment.
1-9 Perform # trials of each test.
^DEL To abort at any time."L ];
TTY.PutCR[user]; TTY.PutCR[user];

DO OPEN tp, TTY;
 BEGIN
 reply: STRING = [100];
 PutString[user, "Type one of D,E,G,H,P,R,S,T,Q,--,1-9 [R1]: "L];
 reply[0] ← 0C;
 CheckAbort;
 GetLine[user, reply ! Rubout, LineOverflow => GOTO TryAgain];
 CheckAbort;
 SELECT reply[0] FROM
  'd, 'D => {checkResults ← FALSE};
  'e, 'E => {useDoradoClock ← countOnlyEmulatorCycles ← TRUE};
  'g, 'G => {EXIT};
  'h, 'H => {useDoradoClock ← TRUE};
  'p, 'P => {spying ← spyOnProcs ← TRUE};
  'r, 'R => {tp ← DefaultTestParameters};
  's, 'S => {spying ← TRUE; showSpyData ← afterAllTests};
  't, 'T => {spying ← TRUE; showSpyData ← afterEachTest};
  'q, 'Q => {GOTO QuitTestProgram};
  '- => {NULL};
  IN ['1..'9] => {trials ← ShortString.StringToDecimal[
    reply ! ShortString.InvalidNumber => GOTO TryAgain ]};
  ENDCASE => GOTO TryAgain;
 EXITS
  TryAgain => PutLine[user, " ???"L];
 END;
 ENDLOOP;

-- Call debugger as needed for spying.
IF tp.spying THEN CallSpy[
IF tp.spyOnProcs THEN startAndWatchDetails ELSE start ];

-- Eliminating timing transients before timings start.
TimeTest.Null[]; TimeTest.Null[]; TimeTest.Null[];

-- Perform testing.
NullTests;
SimpleParameterTests;
SignalTests;
SmallArrayTests;
--BigArrayTests;

-- Call debugger as needed for spying.
IF tp.spying THEN CallSpy[
 (SELECT tp.showSpyData FROM
  afterEachTest => stop,
  afterAllTests => stopAndDisplayStats,
  ENDCASE => ERROR) ];

REPEAT
 QuitTestProgram => NULL;
ENDLOOP; -- Test Loop.

FinishPrecisionTimings;

END; -- TestRoutine.


NullTests: PROC =
BEGIN

-- Timing overhead.

StartTest[testName: "per call timing overhead"L, skipSpy: TRUE];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 --DelayTest;
 StartPrecisionTiming;
 NULL;
 StopPrecisionTiming;
 ENDLOOP;
ENDLOOP;
StopTest[skipSpy: TRUE];

-- Null Procedure

StartTest["the null procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 TimeTest.Null[];
 StopPrecisionTiming;
 ENDLOOP;
ENDLOOP;
StopTest;

END; -- NullTests.


SimpleParameterTests: PROC =
BEGIN

a,b,c,d,e,f,g,h,i,j: INTEGER;

StartTest["a 1 parameter procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 a ← TimeTest.One[test];
 StopPrecisionTiming;
 IF tp.checkResults AND a#test THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

StartTest["a 2 parameter procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 [a,b] ← TimeTest.Two[test+0,test+1];
 StopPrecisionTiming;
 IF tp.checkResults AND (a#test+0 OR b#test+1) THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

StartTest["a 4 parameter procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 [a,b,c,d] ← TimeTest.Four[test+0,test+1,test+2,test+3];
 StopPrecisionTiming;
 IF tp.checkResults AND (a#test+0 OR b#test+1 OR c#test+2 OR d#test+3)
  THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

StartTest["a 10 parameter procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 [a,b,c,d,e,f,g,h,i,j] ← TimeTest.Ten[
  test+0,test+1,test+2,test+3,test+4,
  test+5,test+6,test+7,test+8,test+9 ];
 StopPrecisionTiming;
 IF tp.checkResults AND
  (a#test+0 OR b#test+1 OR c#test+2 OR d#test+3 OR e#test+4
  OR f#test+5 OR g#test+6 OR h#test+7 OR i#test+8 OR j#test+9)
  THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

END; -- SimpleParameterTests.


SignalTests: PROC =
BEGIN

out: INTEGER;

StartTest["signal test (no signalling)"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 out ← TimeTest.SignalTest[ in: test, action: neither
  ! TimeTest.Signal => {SIGNAL ParamsDisagree; CONTINUE} ];
 StopPrecisionTiming;
 IF tp.checkResults AND out#test THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

StartTest["signal test (SIGNAL & RESUME)"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 out ← TimeTest.SignalTest[ in: test, action: signal
  ! TimeTest.Signal => {
  IF tp.checkResults AND in#test THEN SIGNAL ParamsDisagree;
  RESUME[out: 2*in] } ];
 StopPrecisionTiming;
 IF tp.checkResults AND out#2*test THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

StartTest["signal test (ERROR & UNWIND)"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 out ← TimeTest.SignalTest[ in: test, action: error
  ! TimeTest.Signal => {
  IF tp.checkResults AND in#test THEN SIGNAL ParamsDisagree;
  CONTINUE } ];
 StopPrecisionTiming;
 --Checking is never valid after an ERROR since out is undefined.
 ENDLOOP;
ENDLOOP;
StopTest;

END; -- SignalTests.


SmallArrayTests: PROC =
BEGIN

one: TimeTest.Array1 = ALL[1];
four: TimeTest.Array4 = ALL[4];
ten: TimeTest.Array10 = ALL[10];
forty: TimeTest.Array40 = ALL [40];
hundred: TimeTest.Array100 = ALL[100];

array1: TimeTest.Array1;
array4: TimeTest.Array4;
array10: TimeTest.Array10;
array40: TimeTest.Array40;
array100: TimeTest.Array100;

StartTest["1 word array procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 array1 ← TimeTest.OneArray[one];
 StopPrecisionTiming;
 IF tp.checkResults AND one#array1 THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

StartTest["4 word array procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 array4 ← TimeTest.FourArray[four];
 StopPrecisionTiming;
 IF tp.checkResults AND four#array4 THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

StartTest["10 word array procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 array10 ← TimeTest.TenArray[ten];
 StopPrecisionTiming;
 IF tp.checkResults AND ten#array10 THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

StartTest["40 word array procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 array40 ← TimeTest.FortyArray[forty];
 StopPrecisionTiming;
 IF tp.checkResults AND forty#array40 THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

StartTest["100 word array procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 array100 ← TimeTest.HundredArray[hundred];
 StopPrecisionTiming;
 IF tp.checkResults AND hundred#array100 THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

END; -- SmallArrayTests.



-- Primary time logging routines that use Pilot's timer.

StartTest: PROC [testName: String, skipSpy: BOOLEAN�LSE] =
BEGIN OPEN TTY;
CheckAbort;
PutCR[user]; PutCR[user];
PutString[user, "Timing "L]; PutDecimal[user, tp.trials];
PutString[user, " trials of "L];
PutString[user, testName]; TimeStamp[" at "L];
PreparePrecisionTimings;
IF ~skipSpy AND tp.spying THEN CallSpy[startSpy];
StartTimer[reset:TRUE];
END;

StampTest: PROC = INLINE {StopTimer; DoStampTest; StartTimer};

DoStampTest: PROC =
BEGIN OPEN TTY;
PutString[user, "Doing "L];
PutLongDecimal[user, tp.iterationsPerTrial];
TimeStamp[" calls at "L];
END;

DelayTest: PROC =
INLINE BEGIN
StopTimer;
CheckAbort;
Delay[1000];
StartTimer;
END;

StopTest: PROC [skipSpy: BOOLEAN�LSE] =
BEGIN OPEN TTY;
ms, tenths: LONG INTEGER;
StopTimer[];
IF ~skipSpy AND tp.spying THEN CallSpy[stopSpy];
CheckAbort;
[ms, tenths] ←
ConvertTimer[ReadTimer[].elapsed/(tp.trials*LONG[tp.iterationsPerTrial])];
PutString[user, "The average call time (any checking included) was "L];
PutLongDecimal[user, ms]; PutChar[user, '.];
PutLongDecimal[user, tenths]; TimeStamp[" ms at "L];
PrintPrecisionTimings;
IF ~skipSpy AND tp.spying AND tp.showSpyData=afterEachTest
THEN CallSpy[displayStats];
END;

TimeStamp: PROC [herald: String] =
BEGIN OPEN TTY;
timeString: STRING = [100];
CheckAbort;
Time.Append[timeString, Time.Unpack[Time.Current[]]];
PutString[user, herald];
PutString[user, timeString];
PutLine[user, "."L];
END;

AbortTest: PRIVATE ERROR = CODE;
-- Raised by CheckAbort and caught in TestRoutine.

CheckAbort: PROC =
BEGIN OPEN TTY;
IF ~UserAbort[] THEN RETURN;
ResetUserAbort;
PutCR[user];
PutLine[user, "Test aborted..."L];
ERROR AbortTest;
END;


-- Pilot Timing Routines.

SystemTime: TYPE = LONG CARDINAL; -- Same as System.Pulses.
Microseconds: TYPE = LONG CARDINAL;

ReadPilotClock: PROC RETURNS [SystemTime] =
INLINE {RETURN[ LOOPHOLE[System.GetClockPulses[]] ]};

PilotPrecisionRead: PROC RETURNS [--SystemTime-- PrecisionTime] =
INLINE { RETURN[ ReadPilotClock[] ] };

PilotPrecisionConvert: PROC [pt: --SystemTime-- PrecisionTime]
RETURNS [--microseconds:-- Microseconds] =
----INLINE---- {RETURN[ SystemTimeToUsec[pt] ]};


hrStartTime, hrEventTime, hrElapsedTime: SystemTime ← 0;

StartTimer: PROC [reset: BOOLEAN�LSE] =
INLINE BEGIN
IF reset THEN hrElapsedTime ← 0;
hrStartTime ← ReadPilotClock[];
END;

StopTimer: PROC =
INLINE BEGIN
hrEventTime ← ReadPilotClock[] - hrStartTime;
hrElapsedTime ← hrElapsedTime + hrEventTime;
END;

ReadTimer: PROC RETURNS [event, elapsed: SystemTime] =
----INLINE---- BEGIN
RETURN[hrEventTime, hrElapsedTime]
END;

ConvertTimer: PROC [time: SystemTime] RETURNS [ms,tenthsOfMs: LONG INTEGER] =
BEGIN
time ← (SystemTimeToUsec[time]+50)/100;
ms ← time/10;
tenthsOfMs ← time MOD 10;
END;

SystemTimeToUsec: PROC [time: SystemTime] RETURNS [Microseconds] =
INLINE BEGIN
RETURN[ System.PulsesToMicroseconds[System.Pulses[time]] ]
END;


lastDelayFinished: SystemTime ← 0;

Delay: PROC [interval: --Microseconds-- LONG INTEGER] =
-- Spin at least "interval" between executions. There is a startup glitch.
BEGIN
LastIntegerTime: SystemTime = LAST[LONG INTEGER];
DO
grossWait: SystemTime =
SystemTimeToUsec[ReadPilotClock[]-lastDelayFinished];
waitTime: LONG INTEGER =
IF grossWait < LastIntegerTime THEN grossWait ELSE LastIntegerTime;
SELECT interval-waitTime FROM
 < 0 => EXIT;
 < 50 => NULL;
 ENDCASE => Process.Yield;
ENDLOOP;
lastDelayFinished ← ReadPilotClock[];
END;


-- Individual call high precision timing routines.

PrecisionTime: TYPE = LONG CARDINAL;

RingBufferIndex: TYPE = INTEGER[0..StandardIterationsPerTrial);
SortBufferIndex: TYPE = INTEGER[0..LAST[RingBufferIndex]+2);
RingBuffer: TYPE = ARRAY RingBufferIndex OF PrecisionTime;
SortBuffer: TYPE = ARRAY SortBufferIndex OF PrecisionTime;

ptRingIndex: RingBufferIndex ← LAST[RingBufferIndex];
ptRingBuffer: LONG DESCRIPTOR FOR RingBuffer ← DESCRIPTOR [NIL, 0];
ptSortBuffer: LONG DESCRIPTOR FOR SortBuffer ← DESCRIPTOR [NIL, 0];


ptStartTime: PrecisionTime;

StartPrecisionTiming: PROC =
INLINE BEGIN
ptStartTime ← PrecisionRead[];
END;

StopPrecisionTiming: PROC =
INLINE BEGIN
ptStopTime: PrecisionTime = PrecisionRead[];
ptRingBuffer[(ptRingIndex ← (ptRingIndex+1) MOD (LAST[RingBufferIndex]+1))]
  ← (ptStopTime - ptStartTime);
END;

PrecisionRead: PROC RETURNS [--pt:-- PrecisionTime] =
INLINE BEGIN
RETURN[IF tp.useDoradoClock
  THEN DoradoPrecisionRead[] ELSE PilotPrecisionRead[] ];
END;

PrecisionConvert: PROC [pt: PrecisionTime]
RETURNS [--microseconds:-- Microseconds] =
----INLINE---- BEGIN
RETURN[IF tp.useDoradoClock
  THEN DoradoPrecisionConvert[pt] ELSE PilotPrecisionConvert[pt] ];
END;


-- Precision timing routines.

InitPrecisionTimings: PROC =
BEGIN
-- A compiler LENGTH computation bug makes this ugly:
bufferPtr: LONG POINTER TO SortBuffer ← Heap.systemZone.NEW[SortBuffer];
ptSortBuffer ← DESCRIPTOR[bufferPtr, LENGTH[bufferPtr^]];
ptRingBuffer ← DESCRIPTOR [
@ptSortBuffer[FIRST[SortBufferIndex]+1],
LENGTH[LOOPHOLE[NIL, POINTER TO RingBuffer]^] ];
END;

FinishPrecisionTimings: PROC =
BEGIN
bufferPtr: LONG POINTER TO SortBuffer ← BASE[ptSortBuffer];
Heap.systemZone.FREE[@bufferPtr];
ptSortBuffer ← NIL;
ptRingBuffer ← NIL;
END;

PreparePrecisionTimings: PROC =
BEGIN
IF BASE[ptRingBuffer] = NIL THEN ERROR;
ptRingIndex ← LAST[RingBufferIndex];
FOR i: RingBufferIndex IN RingBufferIndex DO ptRingBuffer[i] ← 0 ENDLOOP;
IF tp.useDoradoClock THEN StartDoradoCounters[EmuCycles];
END;

PrintPrecisionTimings: PROC =
BEGIN OPEN TTY;
Range: INTEGER = 10;
PrintRange: PROC [herald: String, startIndex: RingBufferIndex] =
BEGIN
PutString[user, herald];
FOR i: RingBufferIndex IN [startIndex..startIndex+Range) DO
  IF ptRingBuffer[i] > 99999
  THEN PutString[user, " *****"L]
  ELSE PutLongNumber[ h: user, n: ptRingBuffer[i],
  format: [base: 10, zerofill: FALSE, unsigned: TRUE, columns: 6] ];
  ENDLOOP;
PutCR[user];
END; -- PrintRange.
FOR i: RingBufferIndex IN RingBufferIndex DO
ptRingBuffer[i] ← PrecisionConvert[ptRingBuffer[i]];
ENDLOOP;
QuickSort[ptSortBuffer];
PutString[user, "Some instantaneous call times (microseconds) from a "L];
PutDecimal[user, LAST[RingBufferIndex]-FIRST[RingBufferIndex]+1];
PutLine[user, " element ring buffer:"L];
PrintRange[" Fastest times:"L, FIRST[RingBufferIndex]];
PrintRange[" Median times: "L,
  (LAST[RingBufferIndex]-FIRST[RingBufferIndex]-Range)/2];
PrintRange[" Slowest times:"L, LAST[RingBufferIndex]-Range];
END;


-- Dorado 64ns precision counter routines.
-- Modified from [Ivy]<McDaniel>Measurements>Measure.mesa.

doradoCounters: CounterVector ← ZeroCounterVector;

DoradoPrecisionRead: PROC RETURNS [PrecisionTime] =
INLINE BEGIN
ReadDoradoCounters[@doradoCounters];
RETURN[IF tp.countOnlyEmulatorCycles
  THEN doradoCounters.A.low ELSE doradoCounters.B.low ];
END;

DoradoPrecisionConvert: PROC [pt: PrecisionTime]
RETURNS [--microseconds:-- Microseconds] =
----INLINE---- BEGIN
-- Measured clock time was 2*32.2 ns => 644/10000 = 161/2500 usec/cycle
-- Compute slowly to avoid overflow and do rounding.
RETURN[
  (LONG[161]*(pt/LONG[2500]))
 + ( LONG[161]*(pt MOD LONG[2500])+LONG[2500/2] )/LONG[2500] ];
END;

StartDoradoCounters: PROCEDURE [CounterDescriptor] =
MACHINE CODE BEGIN Mopcodes.zMISC, 240B END;

ReadDoradoCounters: PROCEDURE [CounterVectorPtr] =
MACHINE CODE BEGIN Mopcodes.zMISC, 241B END;

StopDoradoCounters: PROCEDURE =
MACHINE CODE BEGIN Mopcodes.zMISC, 242B END;

CounterVector: TYPE = MACHINE DEPENDENT RECORD [
A(0): MACHINE DEPENDENT RECORD [low(0): LONG CARDINAL, high(2): CARDINAL],
B(3): MACHINE DEPENDENT RECORD [low(0): LONG CARDINAL, high(2): CARDINAL] ];

ZeroCounterVector: CounterVector = [[0,0],[0,0]];

CounterVectorPtr: TYPE = LONG POINTER TO CounterVector;

ASelection: TYPE = MACHINE DEPENDENT {
true, hold, procRef, ifuJump, miss, bpa, bpc, bpe };

BSelection: TYPE = MACHINE DEPENDENT {
true, hold, ifuRef, ifuNrdy, miss, bpb, bpc, bpd };

allA, allB, doA, doB: BOOLEAN = TRUE;
emuOnlyA, emuOnlyB, notIS: BOOLEAN = FALSE;

CounterDescriptor: TYPE = MACHINE DEPENDENT RECORD [
instrSet: BOOLEAN,
ignoreBits123: [0..7],
enableA, enableB: BOOLEAN,
ignoreBits67: [0..3],
aSelects: ASelection,
allTasksA: BOOLEAN,
bSelects: BSelection,
allTasksB: BOOLEAN ];

EmuCycles: CounterDescriptor = [notIS,0,doA,doB,0,true,emuOnlyA,true,allB];


-- This Quicksort routine is from Sedgewick's thesis.
-- It is a transliteration of [Maxc2]<Guibas>STSRT.SAI.

QuickSort: PROC [table: LONG DESCRIPTOR FOR ARRAY OF PrecisionTime] =
BEGIN
first: INTEGER = 0;
last: INTEGER = LENGTH[table]-1;
M: INTEGER = 9;
PP, L, R, I, J: INTEGER;
V, TK: PrecisionTime;
STACK: ARRAY [0..50) OF INTEGER;
--Originally: STACK: ARRAY [0:2*(LOG(256/(M+2)) DIV 1)+1] OF INTEGER;

table[first] ← FIRST[PrecisionTime]; table[last] ← LAST[PrecisionTime];
PP ← 0; L ← first+1; R ← last-1;

WHILE PP >=0 DO
I ← L; J ← R+1; V ← table[L];
WHILE I < J DO
  I←I+1; WHILE table[I]<V DO I ← I+1 ENDLOOP;
  J←J-1; WHILE table[J]>V DO J ← J-1 ENDLOOP;
  TK←table[J]; table[J]←table[I]; table[I]←TK;
  ENDLOOP;
table[I]←table[J]; table[J]←table[L]; table[L]←TK;
SELECT TRUE FROM
  R-J > J-L =>
  SELECT TRUE FROM
  R-J <= M => NULL;
  J-L <= M => {L←J+1; LOOP};
  ENDCASE => BEGIN
  PP←PP+2; STACK[PP]←J+1; STACK[PP+1]←R; R←J-1;
  LOOP;
  END;
  J-L <= M => NULL;
  R-J <= M => {R←J-1; LOOP};
  ENDCASE => BEGIN
  PP ← PP+2; STACK[PP]←L; STACK[PP+1]←J-1; L←J+1;
  LOOP;
  END;
L←STACK[PP];
R←STACK[PP+1];
PP←PP-2;
REPEAT
 FINISHED =>
  FOR I IN [2..last) DO
  V←table[I]; J←I-1;
  WHILE table[J]>V DO table[J+1]←table[J]; J←J-1 ENDLOOP;
  table[J+1]←V;
  ENDLOOP;
ENDLOOP;
END;


-- General spy routine, in the hope that the real procedural interface improves.

CallSpy: PROCEDURE[operation: {
   start, startAndWatchDetails,
   startSpy, stopSpy, displayStats,
   stop, stopAndDisplayStats } ] =
BEGIN
SELECT operation FROM
start =>
 Runtime.CallDebugger["Start Spy..."L];
startAndWatchDetails =>
 Runtime.CallDebugger["Start Spy and finger specific procs..."L];
startSpy =>
 SpyClient.StartSpy;
stopSpy =>
 SpyClient.StopSpy;
displayStats =>
 Runtime.CallDebugger["Examine Spy statistics..."L];
stop =>
 Runtime.CallDebugger["Stop Spy..."L];
stopAndDisplayStats =>
 Runtime.CallDebugger["Stop Spy and examine statistics..."L];
ENDCASE => ERROR;
END;


-- Module Initialization.

Process.Detach[FORK TestProcess[]];

END. -- LupineTimeTestDriver.



BigArrayTests: PROC =
BEGIN

fourHundred: TimeTest.Array400 = ALL[400];
thousand: TimeTest.Array1000 = ALL[1000];

array400: TimeTest.Array400;
array1000: TimeTest.Array1000;

StartTest["400 word array procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 array400 ← TimeTest.FourHundredArray[fourHundred];
 StopPrecisionTiming;
 IF tp.checkResults AND fourHundred#array400 THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

StartTest["1000 word array procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 DelayTest;
 StartPrecisionTiming;
 array1000 ← TimeTest.ThousandArray[thousand];
 StopPrecisionTiming;
 IF tp.checkResults AND thousand#array1000 THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;

END; -- BigArrayTests.



-- String and array descriptor test.

Alphabet: TYPE = PACKED ARRAY [0..26) OF CHARACTER;
string1: String = "AbCdEfGhIjKlMnOpQrStUvWxYz"L;
string2: String = "12345678901234567890123456"L;
text: Alphabet;
textDescriptor: TimeTest.RESULTTextDescriptor = DESCRIPTOR[text];

StartTest["26 character string to sequence procedure"L];
THROUGH [0..tp.trials) DO
StampTest;
FOR test: INTEGER IN [0..tp.iterationsPerTrial) DO
 string: String = IF test MOD 2 = 0 THEN string1 ELSE string2;
 DelayTest;
 StartPrecisionTiming;
 TimeTest.StringDescriptor[string, textDescriptor];
 StopPrecisionTiming;
 IF tp.checkResults AND
  LOOPHOLE[@string.text, LONG POINTER TO Alphabet]^
  # LOOPHOLE[BASE[textDescriptor], LONG POINTER TO Alphabet]^
  THEN SIGNAL ParamsDisagree;
 ENDLOOP;
ENDLOOP;
StopTest;
Bob Hagmann February 8, 1985 5:11:08 pm PST
changes to: DIRECTORY