-- Counter.mesa; edited by Sandman on September 21, 1980  7:29 PM  

DIRECTORY
  ControlDefs USING [
    ControlLink, EPRange, FrameHandle, NullFrame, NullReason, Port, StateVector,
    TraceNext, TraceOff, TrapParameter, TrapReason, TrapStatus],
  CoreSwapDefs USING [BBHandle, ExternalStateVector, PuntInfo, UBBPointer],
  CountPrivate USING [ControlRecord, GroupIndex, VersionID],
  FrameDefs USING [MakeCodeResident],
  FrameOps USING [
    Free, GetReturnLink, MyGlobalFrame, MyLocalFrame, SetReturnFrame,
    SetReturnLink],
  ImageDefs USING [AbortMesa],
  Inline USING [BITSHIFT, LongNumber],
  KeyDefs USING [Keys],
  MiscDefs USING [Zero],
  OsStaticDefs USING [OsStatics],
  ProcessDefs USING [DisableInterrupts, EnableInterrupts],
  ProcessOps USING [CurrentPSB],
  SDDefs USING [
    sBreakBlock, sCoreSwap, SD, sGFTLength, sXferTrap, sXferTrapMonitor],
  Storage USING [Words],
  TimingDefs USING [
    Fudge, Fudges, HiLo, Machine, MaxTick, Pair, ReadTime, RealTime],
  TrapOps USING [ReadXTP, ReadXTS, WriteXTS],
  XferCountDefs USING [];

Counter: PROGRAM
  IMPORTS
    FrameDefs, FrameOps, ImageDefs, Inline, MiscDefs, ProcessDefs, Storage,
    TimingDefs, TrapOps
  EXPORTS XferCountDefs =

  BEGIN OPEN TimingDefs, Inline, CountPrivate, ControlDefs, FrameOps;

  WBPort: PORT [POINTER TO CoreSwapDefs.ExternalStateVector];
  machine: Machine;
  fudge: TimingDefs.Fudge;
  activeFudge: TimingDefs.Pair;
  currentState: TimingDefs.HiLo;
  cr: ControlRecord;

  GetFudge: PROCEDURE RETURNS [val: CARDINAL] = INLINE
    BEGIN
    val ← activeFudge.value;
    IF (activeFudge.factor ← activeFudge.factor - 1) = 0 THEN
      BEGIN
      currentState ← SELECT currentState FROM hi => lo, ENDCASE => hi;
      activeFudge ← fudge[currentState];
      END;
    RETURN
    END;

  HandleTraps: PROCEDURE =
    BEGIN
    state: StateVector;
    trapParam: TrapParameter;
    status: TrapStatus;
    frame: FrameHandle;
    ep, i: CARDINAL;
    link: ControlLink;
    previousProcess: UNSPECIFIED;
    exitTime, entryTime: RealTime;
    finish, time, start: LONG CARDINAL;
    reason: TrapReason;
    state ← STATE;
    state.dest ← GetReturnLink[];
    SDDefs.SD[SDDefs.sXferTrap] ← state.source ← FrameOps.MyLocalFrame[];
    ProcessDefs.DisableInterrupts[];
    ProcessDefs.DisableInterrupts[];
    DO
      IF cr.trace THEN TrapOps.WriteXTS[TraceNext];
      previousProcess ← ProcessOps.CurrentPSB↑;
      exitTime ← ReadTime[];
      ProcessDefs.EnableInterrupts[];
      ProcessDefs.EnableInterrupts[];
      TRANSFER WITH state;
      ProcessDefs.DisableInterrupts[];
      ProcessDefs.DisableInterrupts[];
      state ← STATE;
      entryTime ← ReadTime[];
      trapParam ← TrapOps.ReadXTP[];
      status ← TrapOps.ReadXTS[];
      TrapOps.WriteXTS[TraceOff];

      -- Fixup time
      finish ←
	LONG[
	  WITH entryTime.low SELECT machine FROM altoI => low, altoII => low,
	  d0 => low, ENDCASE => 0] + BITSHIFT[entryTime.high.whole, 10] +
	  LOOPHOLE[LongNumber[num[highbits: entryTime.high.high, lowbits: 0]],
	  LONG CARDINAL];

      start ←
	LONG[
	  WITH exitTime.low SELECT machine FROM altoI => low, altoII => low,
	  d0 => low, ENDCASE => 0] + BITSHIFT[exitTime.high.whole, 10] +
	  LOOPHOLE[LongNumber[num[highbits: exitTime.high.high, lowbits: 0]], LONG
	  CARDINAL];

      i ← GetFudge[];
      time ← finish - start;
      IF start > finish THEN time ← time + MaxTick;
      time ← IF time > i THEN time - i ELSE i;
      IF cr.newMeasurement THEN {cr.newMeasurement ← FALSE; cr.gfi ← 0}
      ELSE IF cr.mode = plain THEN {
	cr.times.plain[cr.gfi] ← cr.times.plain[cr.gfi] + time;
	IF ProcessOps.CurrentPSB↑ = previousProcess THEN
	  cr.counts.plain[cr.gfi] ← cr.counts.plain[cr.gfi] + 1
	ELSE time ← time}
      ELSE {
	to, from: GroupIndex;
	to ← cr.groups[cr.gfi];
	from ← cr.groups[cr.prevGfi];
	cr.prevGfi ← cr.gfi;
	cr.times.matrix[to][from] ← cr.times.matrix[to][from] + time;
	IF ProcessOps.CurrentPSB↑ = previousProcess THEN
	  cr.counts.matrix[to][from] ← cr.counts.matrix[to][from] + 1
	ELSE time ← time};
      SELECT (reason ← status.reason) FROM
	other => {
	  SetReturnLink[
	    IF state.source = NullFrame THEN trapParam.link ELSE state.source];
	  link ← trapParam.link;
	  DO
	    SELECT link.tag FROM
	      procedure => BEGIN cr.gfi ← link.gfi; EXIT; END;
	      indirect => link ← link.link↑;
	      frame => BEGIN cr.gfi ← link.frame.accesslink.gfi; EXIT; END;
	      ENDCASE => BEGIN cr.gfi ← 0; EXIT; END;
	    ENDLOOP};
	localCall => {
	  ep ← (trapParam.ep - 2)/2;
	  frame ← state.source;
	  cr.gfi ← frame.accesslink.gfi;
	  trapParam.link ← ControlLink[
	    procedure[
	    tag: procedure, gfi: cr.gfi + ep/EPRange, ep: ep MOD EPRange]];
	  SetReturnFrame[frame]};
	return => {
	  frame ← trapParam.frame - 6;
	  link ← trapParam.link ← frame.returnlink;
	  SetReturnFrame[frame];
	  DO
	    SELECT link.tag FROM
	      procedure => BEGIN cr.gfi ← link.gfi; EXIT; END;
	      indirect => link ← link.link↑;
	      frame => BEGIN cr.gfi ← link.frame.accesslink.gfi; EXIT; END;
	      ENDCASE => BEGIN cr.gfi ← 0; EXIT; END;
	    ENDLOOP};
	ENDCASE;
      IF (cr.process # NIL AND cr.process # ProcessOps.CurrentPSB↑) THEN
	cr.gfi ← 0;
      state.dest ← trapParam.link;
      IF reason = return THEN
	BEGIN FrameOps.Free[frame]; state.source ← NullFrame; END;
      ENDLOOP;
    END;

  TraceOn: TrapStatus = [0, NullReason, 0, on];

  MonitorBreaks: PROCEDURE =
    BEGIN
    state: StateVector;
    frame: FrameHandle;
    esv: CoreSwapDefs.ExternalStateVector;
    bbHandle: CoreSwapDefs.BBHandle;
    ubb: CoreSwapDefs.UBBPointer;
    i: CARDINAL;
    state ← STATE;
    cr.self ← MyLocalFrame[];
    state.dest ← GetReturnLink[];
    ProcessDefs.DisableInterrupts[];
    DO
      ProcessDefs.EnableInterrupts[];
      TRANSFER WITH state;
      ProcessDefs.DisableInterrupts[];
      state ← STATE;
      SetReturnFrame[state.dest ← frame ← state.source];
      state.source ← FrameOps.MyLocalFrame[];
      frame.pc ← [IF frame.pc < 0 THEN -frame.pc ELSE (1 - frame.pc)];
      bbHandle ← SDDefs.SD[SDDefs.sBreakBlock];
      FOR i IN [0..bbHandle.length) DO
	ubb ← @bbHandle.blocks[i];
	IF frame.accesslink = ubb.frame AND frame.pc = ubb.pc THEN
	  BEGIN
	  IF ubb.counterL THEN
	    SELECT LOOPHOLE[ubb.ptrR, CARDINAL] FROM
	      0 => cr.trace ← TRUE;
	      1 => cr.trace ← ~cr.trace;
	      2 => cr.trace ← FALSE;
	      ENDCASE;
	  state.instbyte ← ubb.inst;
	  EXIT;
	  END;
	REPEAT
	  FINISHED =>
	    BEGIN
	    esv ← CoreSwapDefs.PuntInfo↑.puntESV;
	    esv.state ← @state;
	    esv.reason ← worrybreak;
	    DO
	      WBPort[@esv];
	      SELECT esv.reason FROM
		proceed => EXIT;
		kill => ImageDefs.AbortMesa[];
		showscreen => UNTIL KeyDefs.Keys.Spare3 = down DO NULL ENDLOOP;
		ENDCASE;
	      esv.reason ← return;
	      ENDLOOP;
	    END;
	ENDLOOP;
      IF cr.trace THEN TrapOps.WriteXTS[TraceNext]
      ELSE BEGIN cr.gfi ← 0; TrapOps.WriteXTS[TraceOff] END;
      ENDLOOP;
    END;

  StartCounting: PUBLIC PROCEDURE =
    BEGIN TrapOps.WriteXTS[TraceNext]; cr.trace ← TRUE; RETURN END;

  StopCounting: PUBLIC PROCEDURE = BEGIN cr.trace ← FALSE; cr.gfi ← 0; RETURN END;

  Init: PROCEDURE =
    BEGIN
    sd: POINTER TO ARRAY [0..0) OF UNSPECIFIED ← SDDefs.SD;
    length: CARDINAL = MAX[SDDefs.SD[SDDefs.sGFTLength], 256];
    words: CARDINAL = length*SIZE[LONG CARDINAL]*2 + length;
    data: POINTER = Storage.Words[words];
    FrameDefs.MakeCodeResident[FrameOps.MyGlobalFrame[]];
    cr ←
      [gfi: 0, version: VersionID, saveBreakHandler: NIL, length: length,
	newSession: TRUE, trace: FALSE, counts: data, self: NIL, mode: plain,
	times: data + length*SIZE[LONG CARDINAL], prevGfi: 0, process: NIL,
	groups: data + 2*length*SIZE[LONG CARDINAL], pulseConversion: 3810,
        newMeasurement: TRUE];
    MiscDefs.Zero[data, words];
    sd[SDDefs.sXferTrapMonitor] ← @cr;
    LOOPHOLE[WBPort, ControlDefs.Port] ←
      [representation[in: 0, out: sd[SDDefs.sCoreSwap]]];
    SELECT OsStaticDefs.OsStatics.AltoVersion.engineeringnumber FROM
      2, 3, 5 => BEGIN machine ← altoII; fudge ← TimingDefs.Fudges[altoII] END;
      4 => BEGIN machine ← d0; fudge ← TimingDefs.Fudges[d0] END;
      ENDCASE => BEGIN machine ← altoI; fudge ← TimingDefs.Fudges[altoI] END;
    activeFudge ← fudge[currentState ← hi];
    HandleTraps[];
    MonitorBreaks[];
    END;

  Init[];

  END...