-- SageImpl.mesa
-- Last Edited by: Barth, February 8, 1985 6:00:19 pm PST

DIRECTORY DicentraInputOutput, Heap, Sage;

SageImpl: PROGRAM 
  IMPORTS DicentraInputOutput, Heap
  EXPORTS Sage =

BEGIN OPEN DicentraInputOutput, Sage;

  z:UNCOUNTED ZONE = Heap.systemZone;

Error: PUBLIC ERROR [why: ErrorReason] = CODE;

ConcreteTester: TYPE = LONG POINTER TO TesterRec;
TesterRec: PUBLIC TYPE = RECORD[
  bigChip: BOOL ← FALSE,
  taps: PulseLineTaps,
  control: ChannelControlData,
  initialData: ChannelVector,
  pulseGenCounterRegState: PulseGenCounterReg ← [0, 0],
  pulseGenCounterCtlRegState: PulseGenCounterCtlReg ← [0, FALSE, FALSE],
  pulseGenCtlRegState: PulseGenCtlReg ← [0, FALSE, FALSE, FALSE]];

PulseGenCounterReg: TYPE = MACHINE DEPENDENT RECORD[
  null(0: 0..11): [0..4096) ← 0,
  counter(0: 12..15): [0..16) ← 0];
  
PulseGenCounterCtlReg: TYPE = MACHINE DEPENDENT RECORD[
  null(0: 0..13): [0..16384) ← 0,
  continuous(0: 14..14): BOOL ← FALSE,
  go(0: 15..15): BOOL ← FALSE];
  
PulseGenCtlReg: TYPE = MACHINE DEPENDENT RECORD[
  null(0: 0..12): [0..8192) ← 0,
  nReset(0: 13..13): BOOL ← FALSE,
  feedBack15(0: 14..14): BOOL ← FALSE,
  globalHold(0: 15..15): BOOL ← FALSE];
  
ChannelCtlReg: TYPE = MACHINE DEPENDENT RECORD[
  null(0: 0..7): [0..256) ← 0,
  localHold(0: 8..8): BOOL ← FALSE,
  inPulseLine(0: 9..12): PulseLine ← 0,
  outPulsePair(0: 13..15): PulsePair ← 0];

MultibusAddress: TYPE = LONG CARDINAL;
SageDeviceAddress: MultibusAddress = 4000H;
PulseGenCtlRegAddress: MultibusAddress = SageDeviceAddress+11H;
PulseGenCounterRegHackAddress: MultibusAddress = SageDeviceAddress+01E0H;
PulseGenCounterCtlRegAddress: MultibusAddress = SageDeviceAddress+12H;
PulseGenCounterCtlRegHackAddress: MultibusAddress = SageDeviceAddress+01D0H;
PulseGenTapAddress: MultibusAddress = SageDeviceAddress+0H;
PulseGenCounterRegAddress: MultibusAddress = SageDeviceAddress+10H;
ChannelAddress: MultibusAddress = SageDeviceAddress+20H;

ControlValue: TYPE = {
  continuous, go, reset, feedBack15, globalHold};
  
InputCount: CARDINAL = 8;
InputData: TYPE = MACHINE DEPENDENT RECORD[
  null(0: 0..InputCount-1): [0..256),
  bits(0: InputCount..15): PACKED ARRAY InputIndex OF BOOL];
InputIndex: TYPE = [0..InputCount);

InitializeTester: PUBLIC PROC [pulseTaps: PulseLineTaps, channelControl: ChannelControlData, initialData: ChannelVector, feedBack15: BOOL ← FALSE] RETURNS [t: ConcreteTester] = {
  t ← z.NEW[TesterRec ← [FALSE, pulseTaps, channelControl, initialData]];
  { OPEN t;
    taps ← z.NEW[PulseLineTapRec ← pulseTaps↑];
    control ← z.NEW[ChannelControlDataRec ← channelControl↑];
    initialData ← z.NEW[ChannelVectorRec ← initialData↑];
    pulseGenCtlRegState.feedBack15 ← feedBack15;
    DoOutput[LOOPHOLE[pulseGenCounterRegState], PulseGenCounterRegHackAddress];
    DoOutput[LOOPHOLE[pulseGenCounterCtlRegState], PulseGenCounterCtlRegHackAddress];
    DoOutput[LOOPHOLE[pulseGenCtlRegState], PulseGenCtlRegAddress];
    SetControl[t, go, FALSE];
    SetControl[t, continuous];
    SetControl[t, globalHold];
    FOR pl: PulseLine IN PulseLine DO
      SetPulseLineTap[pulseLine: pl, tap: taps[pl]];
      ENDLOOP;
    FOR c: Channel IN Channel DO
      SetChannelCtlReg[t: t, channel: c, register: [localHold: TRUE, inPulseLine: control[c].inputPulseLine, outPulsePair: control[c].outputPulsePair]];
      ENDLOOP;
    LoadOutputShiftRegisters[t, initialData];
    -- If the following loop takes too long the data in the shift registers may die because of the delay between reset of local hold and the start of the clocks.  A change in the tester hardware is needed to avoid this race.  A line should be added from the channel control register to all the channel chips which forces all the channels to be static regardless of the value of local hold.
    FOR c: Channel IN Channel DO
      SetChannelCtlReg[t: t, channel: c, register: [localHold: control[c].localHold, inPulseLine: control[c].inputPulseLine, outPulsePair: control[c].outputPulsePair]];
      ENDLOOP;
    SetControl[t, globalHold, FALSE];
    SetControl[t, globalHold];
    SetControl[t, reset];
    SetControl[t, reset, FALSE];
    SetControl[t, go];
    };
  RETURN[t];
  };
  
FinalizeTester: PUBLIC PROC [t: ConcreteTester] = {
  z.FREE[@t.taps];
  z.FREE[@t.control];
  z.FREE[@t.initialData];
  z.FREE[@t];
  };
  
Run: PUBLIC PROC [t: ConcreteTester, runData: ChannelSequence] = {
  FOR s: CARDINAL IN [0..runData.loadCount) DO
    v: ChannelVector ← @runData.loads[s];
    LoadOutputShiftRegisters[t, v];
    SetControl[t, continuous, FALSE];
    SetControl[t, go, FALSE];
    SetCounter[t, 16 - v.testerCycles];
    SetControl[t, globalHold, FALSE];
    SetControl[t, reset];
    SetControl[t, reset, FALSE];
    SetControl[t, go];
    DO
      foo: PACKED ARRAY [0..16) OF BOOL ← LOOPHOLE[ Input[ LOOPHOLE[
	   PulseGenCounterCtlRegHackAddress]]];
	   IF foo[15] THEN EXIT;
	   ENDLOOP;
    SetControl[t, continuous];
    SetControl[t, globalHold];
    SetControl[t, go, FALSE];
    SetControl[t, go];
    FOR c: CARDINAL ← 0, c+InputCount UNTIL c>LAST[Channel] DO
      FOR i: InputStage IN InputStage DO
        data: InputData ← ReadChannelOctetData[t, c];
        FOR nc: InputIndex IN InputIndex DO
          v.stageData[c+nc].sense[i] ← data.bits[nc];
          ENDLOOP;
        ENDLOOP;
      ENDLOOP;
    ENDLOOP;
  };
  
LoadOutputShiftRegisters: PUBLIC PROC [t: ConcreteTester, v: ChannelVector] = {
  IF v=NIL THEN ERROR Error[nilChannelVector];
  FOR c: CARDINAL ← 0, c+2 UNTIL c>LAST[Channel] DO
    FOR i: CARDINAL ← 0, i+2 UNTIL i>LAST[OutputStage] DO
      dl0, dh0, dl1, dh1: DriveState;
      dl0 ← v.stageData[c].force[i];
      dh0 ← v.stageData[c].force[i+1];
      dl1 ← v.stageData[c+1].force[i];
      dh1 ← v.stageData[c+1].force[i+1];
      IF i MOD 4 #0 THEN {
        t: DriveState ← dl0; dl0 ← dh0; dh0 ← t;
        t ← dl1; dl1 ← dh1; dh1 ← t;
        };
      WriteChannelPairData[t: t, channel0: c, dataLow0: dl0, dataHigh0: dh0, dataLow1: dl1, dataHigh1: dh1];
      ENDLOOP;
    ENDLOOP;
  };
  
SetControl: PROCEDURE [t: ConcreteTester, value: ControlValue, newValue: BOOL ← TRUE] = {
  OPEN t;
  SELECT value FROM
    continuous => pulseGenCounterCtlRegState.continuous ← newValue;
    go => pulseGenCounterCtlRegState.go ← newValue;
    reset => pulseGenCtlRegState.nReset ← NOT newValue;
    feedBack15 => pulseGenCtlRegState.feedBack15 ← newValue;
    globalHold => pulseGenCtlRegState.globalHold ← newValue;
    ENDCASE => ERROR;
  IF value IN [continuous .. go] THEN DoOutput[LOOPHOLE[pulseGenCounterCtlRegState], PulseGenCounterCtlRegHackAddress] ELSE DoOutput[LOOPHOLE[pulseGenCtlRegState], PulseGenCtlRegAddress];
  };
  
SetPulseLineTap: PROCEDURE [pulseLine: PulseLine, tap: Tap] = {
  DoOutput[tap, PulseGenTapAddress+pulseLine];
  };
  
SetChannelCtlReg: PROCEDURE [t: ConcreteTester, channel: Channel, register: ChannelCtlReg] = {
  DoOutput[LOOPHOLE[register], ChannelAddress + (channel/(IF t.bigChip THEN 32 ELSE 16))*(IF t.bigChip THEN 40H ELSE 20H) + (IF t.bigChip THEN 20H ELSE 10H) + channel MOD (IF t.bigChip THEN 32 ELSE 16)];
  };
  
WriteChannelPairData: PROCEDURE [t: ConcreteTester, channel0: Channel, dataLow0, dataHigh0, dataLow1, dataHigh1: DriveState] = {
  dataLow: CARDINAL ← ConvertToBits[dataLow0, dataHigh0];
  dataHigh: CARDINAL ← ConvertToBits[dataLow1, dataHigh1];
  IF t.bigChip THEN DoOutput[16*dataLow + dataHigh,
    ChannelAddress + (channel0/32)*40H + channel0 MOD 32]
  ELSE {
    channelAddress: LONG CARDINAL ← ChannelAddress + (channel0/16)*20H + channel0 MOD 16;
    IF (channel0 MOD 16) < 8 THEN DoOutput[16*dataLow + dataHigh, channelAddress]
    ELSE DoOutput[dataLow + 16*dataHigh, channelAddress];
    };
  };
  
ConvertToBits: PROC[dataLow, dataHigh: DriveState] RETURNS [dataOut: CARDINAL] = {
-- 01 => H, 10 => L, 11 => T, dataLow goes in bits 1 and 3 of a nibble,
-- dataHigh goes in bits 0 and 2 of the nibble, numbering the nibble bits
-- from left to right, 0 to 3, high order to low order of course.
  Bits: ARRAY DriveState OF ARRAY DriveState OF CARDINAL = [[0CH, 0EH, 06H], [0DH, 0FH, 07H], [09H, 0BH, 03H]];
  RETURN[Bits[dataLow][dataHigh]];
  };
  
SetCounter: PROCEDURE [t: ConcreteTester, value: CARDINAL] = {
  t.pulseGenCounterRegState.counter ← value;
  DoOutput[LOOPHOLE[t.pulseGenCounterRegState], PulseGenCounterRegHackAddress];
  };

ReadChannelOctetData: PROCEDURE [t: ConcreteTester, channel0: Channel] RETURNS [InputData] = {
  RETURN[ LOOPHOLE [DoInput[ChannelAddress + (channel0/(IF t.bigChip THEN 32 ELSE 16))*(IF t.bigChip THEN 40H ELSE 20H) + ((channel0 MOD (IF t.bigChip THEN 32 ELSE 16)) / 8) ]]];
  };
  
DoOutput: PROCEDURE [word: CARDINAL, address: LONG CARDINAL] = {
  DicentraInputOutput.Output[LOOPHOLE[word, UNSPECIFIED], LOOPHOLE[address]];
  };
  
DoInput: PROCEDURE [address: LONG CARDINAL] RETURNS [word: CARDINAL] = {
  RETURN[DicentraInputOutput.Input[LOOPHOLE[address]]];
  };
  
END.