Procedures
Run: 
PUBLIC 
PROC [fixture: Fixture] = {
None of the routines are parameterized by the chip number!
InitSystem[];
Reset[];
SetClock[NARROW[RProperties.GetProp[fixture.properties, $Clock], REF INT]^];
IF hardwareEnabled THEN FindTZero[];
FOR chipIndex: 
CARDINAL 
IN [0..fixture.size) 
DO
chip: Chip ← fixture[chipIndex];
FOR ch: ChannelIndex 
IN ChannelIndex 
DO
tg: TimingGroup ← chip.formatTiming[ch];
IF tg#
NIL 
THEN {
IF hardwareEnabled 
THEN 
SELECT tg.format 
FROM
NRZ => NRZMode[ch, tg.delay];
RZ => RZMode[ch, tg.delay, tg.width];
RO => ROMode[ch, tg.delay, tg.width];
RT => RTMode[ch, tg.delay, tg.width];
RC => RCMode[ch, tg.delay, tg.width];
ENDCASE => ERROR;
 
SetAnalogSample[ch, tg.variableThreshold];
SetTTLLevels[ch, tg.variableLevels];
};
 
ENDLOOP;
 
FOR vi: 
CARD 
IN [0..chip.firstFreeVector) 
DO
RamWrite[VRam, vi, chip.vectors[vi].elements[0], chip.vectors[vi].elements[1], chip.vectors[vi].elements[2], chip.vectors[vi].elements[3]];
ENDLOOP;
 
FOR imi: InhibitMaskIndex 
IN InhibitMaskIndex 
DO
data0, data1, data2, data3: Byte10 ← 0;
FOR ch: ChannelIndex 
IN ChannelIndex 
DO
SELECT ch 
FROM
IN [0..6) => {
data0 ← BitOps.IBIW[chip.inhibitMask[imi][ch].mask, data0, ch, 6];
data2 ← BitOps.IBIW[chip.inhibitMask[imi][ch].inhibit, data2, ch, 6];
};
 
IN [6..16) => {
data1 ← BitOps.IBIW[chip.inhibitMask[imi][ch].mask, data1, ch-6, 10];
data3 ← BitOps.IBIW[chip.inhibitMask[imi][ch].inhibit, data3, ch-6, 10];
};
 
ENDCASE => ERROR;
 
ENDLOOP;
 
RamWrite[CRam, imi, data0, data1, data2, data3];
ENDLOOP;
 
CBusWrite[writeEndAdd, chip.matchAddress];
CBusWrite[writeLoopAdd, chip.targetAddress];
ENDLOOP;
 
StartCtl[TRUE, TRUE, FALSE];
};
 
InitSystem: 
PROC ~ {
IF gDebug THEN TerminalIO.PutRope["\nInit\n"];
InitGPIBDevices[];
SetRefDelayAbs[0];
CBusWrite[clkCtl, 0];
gClockCtl ← 0;
SetClock[100];
};
 
Reset: 
PROC ~ {
IF gDebug THEN TerminalIO.PutRope["Reset\n"];
StartCtl[FALSE, FALSE, FALSE];
CBusWrite[writeLoopAdd, 0];
CBusWrite[writeEndAdd, 0];
CBusWrite[writeExtRamCtl, 0];
StartCtl[FALSE, FALSE, TRUE];
StartCtl[FALSE, FALSE, FALSE];
};
 
FindTZero: 
PROC ~ {
In RZ mode force the output of the pulse generator high so that only the transitions resulting from changes in the data pipeline are observed.  Skew RefCk to measure the time when the data transition occurs.  Measure both rising and falling edges and call the greater of the two times the beginning of the cycle.
biggest: ChannelIndex ← 0;
new: Ns ← 0;
gCycOffset ← 0;
ClearAllPE[];
SetDataPattern[p10];
FOR ch: ChannelIndex 
IN ChannelIndex 
DO
SetFormat[ch, RZ];
ForcePulseHigh[ch];
new ← MAX[FindOutputEdge[ch, Even, Up], FindOutputEdge[ch, Odd, Down]];
IF new>gCycOffset THEN {gCycOffset ← new; biggest ← ch};
ENDLOOP;
 
TerminalIO.PutF["Cycle Start Offset = %g, chan: %g\n", IO.int[gCycOffset], IO.int[biggest]];
};
 
NRZMode: 
PROC [ch: ChannelIndex, delay: Delay] ~ {
dhc, whc: HalfClocks ← 0;
dis, wis: InvStages ← 0;
delayFix: NAT ← 0;
TerminalIO.PutF["Chan: %g, NRZ, Delay: %g\n", IO.int[ch], IO.int[delay]];
[dhc, dis] ← CalibrateDL[ch, Delay, delay];
[whc, wis] ← CalibrateDL[ch, Width, delay+20]; --doesn't matter how wide
MakePulse[ch, NRZ, dhc, dis, whc, wis];
DO
WHILE HoneEdge[ch, Leading, Even, Up, delay, p10] 
DO
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, NRZ, dhc, dis, whc, wis];
ENDLOOP;
 
IF HoneEdge[ch, Leading, Odd, Up, delay, p01] 
THEN {
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, NRZ, dhc, dis, whc, wis];
LOOP;
};
 
EXIT;
ENDLOOP;
 
SetDataPattern[p10]; --Display the result
SetRefDelay[0];
};
 
RZMode: 
PROC [ch: ChannelIndex, delay: Delay, width: Width] ~ {
dhc, whc: HalfClocks ← 0;
dis, wis: InvStages ← 0;
delayFix, widthFix: NAT ← 0;
TerminalIO.PutF["Chan: %g, RZ, Delay: %g, Width: %g\n", IO.int[ch], IO.int[delay], IO.int[width]];
[dhc, dis] ← CalibrateDL[ch, Delay, delay];
[whc, wis] ← CalibrateDL[ch, Width, delay+width];
MakePulse[ch, RZ, dhc, dis, whc, wis];
DO
WHILE HoneEdge[ch, Leading, Even, Up, delay, p10] 
DO
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, RZ, dhc, dis, whc, wis];
ENDLOOP;
 
WHILE HoneEdge[ch, Trailing, Even, Down, delay+width, p10] 
DO
IF (widthFix ← widthFix+1) > 7 THEN ERROR;
[whc, wis] ← CalibrateDL[ch, Width, delay+width-widthFix];
MakePulse[ch, RZ, dhc, dis, whc, wis];
ENDLOOP;
 
IF HoneEdge[ch, Leading, Odd, Up, delay, p01] 
THEN {
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, RZ, dhc, dis, whc, wis];
LOOP;
};
 
IF HoneEdge[ch, Trailing, Odd, Down, delay+width, p01] 
THEN {
IF (widthFix ← widthFix+1) > 7 THEN ERROR;
[whc, wis] ← CalibrateDL[ch, Width, delay+width-widthFix];
MakePulse[ch, RZ, dhc, dis, whc, wis];
LOOP;
};
 
EXIT;
ENDLOOP;
 
SetDataPattern[p11]; --Display the result
SetRefDelay[0];
};
 
ROMode: 
PROC [ch: ChannelIndex, delay: Delay, width: Width] ~ {
dhc, whc: HalfClocks ← 0;
dis, wis: InvStages ← 0;
delayFix, widthFix: NAT ← 0;
TerminalIO.PutF["Chan: %g, RO, Delay: %g, Width: %g\n", IO.int[ch], IO.int[delay], IO.int[width]];
[dhc, dis] ← CalibrateDL[ch, Delay, delay];
[whc, wis] ← CalibrateDL[ch, Width, delay+width];
MakePulse[ch, RO, dhc, dis, whc, wis];
DO
WHILE HoneEdge[ch, Leading, Even, Down, delay, p01] 
DO
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, RO, dhc, dis, whc, wis];
ENDLOOP;
 
WHILE HoneEdge[ch, Trailing, Even, Up, delay+width, p01] 
DO
IF (widthFix ← widthFix+1) > 7 THEN ERROR;
[whc, wis] ← CalibrateDL[ch, Width, delay+width-widthFix];
MakePulse[ch, RO, dhc, dis, whc, wis];
ENDLOOP;
 
IF HoneEdge[ch, Leading, Odd, Down, delay, p10] 
THEN {
IF (delayFix ← delayFix+1) > 7 THEN ERROR;
[dhc, dis] ← CalibrateDL[ch, Delay, delay-delayFix];
MakePulse[ch, RO, dhc, dis, whc, wis];
LOOP;
};
 
IF HoneEdge[ch, Trailing, Odd, Up, delay+width, p10] 
THEN {
IF (widthFix ← widthFix+1) > 7 THEN ERROR;
[whc, wis] ← CalibrateDL[ch, Width, delay+width-widthFix];
MakePulse[ch, RO, dhc, dis, whc, wis];
LOOP;
};
 
EXIT;
ENDLOOP;
 
SetDataPattern[p00]; --Display the result
SetRefDelay[0];
};
 
RTMode: 
PROC [ch: ChannelIndex, delay: Delay, width: Width] ~ {
RZMode[ch, delay, width];
SetFormat[ch, RT];
};
 
RCMode: 
PROC [ch: ChannelIndex, delay: Delay, width: Width] ~ {
RZMode[ch, delay, width];
SetFormat[ch, RC];
};
 
MakePulse: 
PROC [ch: ChannelIndex, f: Format, dhc: HalfClocks, dis: InvStages, whc: HalfClocks, wis: InvStages] ~ {
SetFormat[ch, f];
SetTimingChan[ch, Delay, dhc, dis];
SetTimingChan[ch, Width, whc, wis];
};
 
FindHC: 
PROC [ch: ChannelIndex, tg: Timing, t: Transition, start: HalfClocks ← 0] 
RETURNS [hc: HalfClocks ← 0] ~ {
SetTimingChan[ch, tg, 0, 0];
hc ← start;
WHILE 
NOT EdgeIsEarly[ch, t] 
DO
SetTimingChan[ch, tg, (hc←hc+1), 0];
ENDLOOP;
 
WHILE EdgeIsEarly[ch, t] 
DO
SetTimingChan[ch, tg, (hc←hc+1), 0];
ENDLOOP;
 
};
 
FindIS: 
PROC [ch: ChannelIndex, tg: Timing, hc: HalfClocks, t: Transition] 
RETURNS [is: InvStages ← 0]~ {
SetTimingChan[ch, tg, hc, 0];
WHILE EdgeIsEarly[ch, t] 
DO
SetTimingChan[ch, tg, hc, (is←is+1)];
ENDLOOP;
 
};
 
CalibrateDL: 
PROC [ch: ChannelIndex, tg: Timing, ns: Ns] 
RETURNS [hc: HalfClocks, is: InvStages] ~ {
Cal: 
PROC [t: Transition] 
RETURNS [hc: HalfClocks ← 0, is: InvStages ← 0] ~ {
IF (hc ← FindHC[ch, tg, t]) = 0 THEN hc ← FindHC[ch, tg, t, 1];
hc ← hc-1;
IF (is ← FindIS[ch, tg, hc, t]) = 0 
THEN 
IF hc#0 
THEN {
hc ← hc-1;
is ← FindIS[ch, tg, hc, t];
} ELSE {
hc ← FindHC[ch, tg, t, 2]-1;
is ← FindIS[ch, tg, hc, t];
IF is=0 THEN ERROR;
};
 
 
is←is-1;
SetTimingChan[ch, tg, hc, is];
};
 
hce, hco: HalfClocks;
ise, iso: InvStages;
SetRefPhase[Even];
SetDataPattern[p11];
SetFormat[ch, RZ];
IF tg=Delay THEN SetDelayPassThru[ch] ELSE SetWidthPassThru[ch];
SetRefDelay[ns];
[hce, ise] ← Cal[Up];
SetRefPhase[Odd];
[hco, iso] ← Cal[Down];
[hc, is] ← MinDelay[hce, ise, hco, iso];
IF gDebug THEN TerminalIO.PutF["Half Clocks: %g, Inv Stages: %g\n", IO.int[hc], IO.int[is]];
};
 
HoneEdge: 
PROC [ch: ChannelIndex, e: Edge, p: Phase, t: Transition, ns: Ns, pat: DataPattern] 
RETURNS [tooLate: 
BOOL ← 
FALSE] ~ {
ec: EdgeC ← 0;
SetRefPhase[p];
SetRefDelay[ns];
SetDataPattern[pat];
FOR i: 
NAT 
IN EdgeC 
DO
TweakEdge[ch, e, p, (ec ← i), 0];
Process.PauseMsec[pause];
IF NOT EdgeIsEarly[ch, t] THEN EXIT;
ENDLOOP;
 
IF ec#0 
THEN TweakEdge[ch, e, p, (ec ← ec-1), 0] 
ELSE {
TerminalIO.PutF["Too early\n"];
RETURN[TRUE];
};
 
FOR i: 
NAT 
IN EdgeF 
DO
TweakEdge[ch, e, p, ec, i];
Process.PauseMsec[pause];
IF NOT EdgeIsEarly[ch, t] THEN GOTO foundIt;
REPEAT
foundIt => {TerminalIO.PutF[" %g %g EdgeC: %g, EdgeF: %g\n", 
IO.rope[IF e=Leading THEN "Leading" ELSE "Trailing"],
IO.rope[IF p=Even THEN "Even" ELSE "Odd"],
IO.int[ec], IO.int[i]]};
 
FINISHED => TerminalIO.PutF["Exceeded range\n"];
 
ENDLOOP;
 
};
 
TweakEdge: 
PROCEDURE [ch: ChannelIndex, e: Edge, p: Phase, ec: EdgeC, ef: EdgeF] = {
PEWrite[ch, IF p=Odd THEN risingEdge ELSE fallingEdge, MapEdge[ec, ef], IF e=Leading THEN Delay ELSE Width];
};
 
ClearAllPE: 
PROC ~ {
FOR c: ChannelIndex 
IN ChannelIndex 
DO
ClearPEChan[c];
ENDLOOP;
 
};
 
ClearPEChan: 
PROC [c: ChannelIndex] ~ {
FOR t: Timing 
IN Timing 
DO
SetTimingChan[c, t, 0, 0];
ENDLOOP;
 
PEWrite[c, ioCtl, 0];
PEWrite[c, format, nrz];
};
 
MinDelay: 
PROC [hc0: HalfClocks, is0: InvStages, hc1: HalfClocks, is1: InvStages] 
RETURNS [hs: HalfClocks, is: InvStages] ~ {
SELECT 
TRUE 
FROM
hc0>hc1 => RETURN[hc1, is1];
hc0<hc1 => RETURN[hc0, is0];
hc0=hc1 AND is0>is1 => RETURN[hc1, is1];
hc0=hc1 AND is0<is1 => RETURN[hc0, is0];
ENDCASE => RETURN[hc0, is0];
 
};
 
SetTimingChan: 
PROC [chan: ChannelIndex, timing: Timing, halfClocks: HalfClocks, invStages: InvStages, upEdgeC: EdgeC ← 0, upEdgeF: EdgeF ← 0, dnEdgeC: EdgeC ← 0, dnEdgeF: EdgeF ← 0] ~ {
PEWrite[chan, dsr0, DSR[halfClocks].high, timing];
PEWrite[chan, dsr1, DSR[halfClocks].low, timing];
PEWrite[chan, invCh0, IC[invStages, FALSE, FALSE].high, timing];
PEWrite[chan, invCh1, IC[invStages, FALSE, FALSE].mid, timing];
PEWrite[chan, invCh2, IC[invStages, FALSE, FALSE].low, timing];
PEWrite[chan, risingEdge, MapEdge[upEdgeC, upEdgeF], timing];
PEWrite[chan, fallingEdge, MapEdge[dnEdgeC, dnEdgeF], timing];
};
 
DSR: 
PROC [halfClocks: HalfClocks] 
RETURNS [high, low: 
CARDINAL] ~ {
c: BitOps.BitDWord  ← 0;
c ← BitOps.IBID[TRUE, c, halfClocks/2, 12];
c ← BitOps.IBID[TRUE, c, IF (halfClocks MOD 2)=0 THEN 10 ELSE 11, 12];
high ← BitOps.ECFD[c, 0, 8, 12];
low ← Basics.BITSHIFT[BitOps.ECFD[c, 8, 4, 12], 4];  --left justify result
};
 
IC: 
PROC [invStages: InvStages, forceH, forceL: 
BOOL] 
RETURNS [high, mid, low: 
CARDINAL] ~ {
c: BitOps.BitDWord ← 0;
c ← BitOps.IBID[TRUE, c, invStages, 22];
IF forceL THEN c ← BitOps.IBID[TRUE, c, 20, 22];
IF forceH THEN c ← BitOps.IBID[TRUE, c, 21, 22];
high ← BitOps.ECFD[c, 0, 8, 22];
mid ← BitOps.ECFD[c, 8, 8, 22];
low ← Basics.BITSHIFT[BitOps.ECFD[c, 16, 6, 22], 2];  --left justify result
};
 
MapEdge: 
PROC [edgeC: EdgeC, edgeF: EdgeF] 
RETURNS [
CARDINAL] ~ {
RETURN[Basics.BITOR[Basics.BITSHIFT[80h, -edgeC], fineMap[edgeF]]];
};
 
RamWrite: 
PROC [which: Ram, add: Address, d0, d1, d2, d3: Byte10] ~ {
IF gRamDebug 
THEN TerminalIO.PutRope[
IO.PutFLR["Write %g Add: %x, Data: %x, %x, %x, %x\n", 
LIST[
IO.rope[
SELECT which 
FROM
VRam => "VRam",
HRam => "HRam",
ERam => "ERam",
CRam => "CRam",
ENDCASE => ERROR],
 
IO.card[add], IO.card[d0], IO.card[d1], IO.card[d2], IO.card[d3]]]];
 
CBusWrite[writeExtRamAdd, add];
LoadRamWD[d0, d1, d2, d3];
CBusWrite[writeExtRamCtl, extRamAccess];
CBusWrite[writeExtRamCtl, 
SELECT which 
FROM
VRam => writeVRam,
HRam => writeHRam,
ERam => writeERam,
CRam => writeCRam,
ENDCASE => ERROR];
 
CBusWrite[writeExtRamCtl, extRamAccess];
CBusWrite[writeExtRamCtl, 0];
};
 
RamRead: 
PROC [which: Ram, add: Address] 
RETURNS [d0, d1, d2, d3: Byte10] ~ {
CBusWrite[writeExtRamAdd, add];
CBusWrite[writeExtRamCtl, extRamAccess];
CBusWrite[writeExtRamCtl, 
SELECT which 
FROM
VRam => readVRam,
HRam => readHRam,
ERam => readERam,
CRam => readCRam,
ENDCASE => ERROR];
 
[d0, d1, d2, d3] ← FetchRamRD[];
CBusWrite[writeExtRamCtl, 0];
IF gRamDebug 
THEN TerminalIO.PutRope[
IO.PutFLR["Read %g Add: %x, Data: %x, %x, %x, %x\n", 
LIST[
IO.rope[
SELECT which 
FROM
VRam => "VRam",
HRam => "HRam",
ERam => "ERam",
CRam => "CRam",
ENDCASE => ERROR],
 
IO.card[add], IO.card[d0], IO.card[d1], IO.card[d2], IO.card[d3]]]];
 
};
 
LoadRamWD: 
PROC [d0, d1, d2, d3: Byte10] ~ {
CBusWrite[ramWD0, d0];
CBusWrite[ramWD1, d1];
CBusWrite[ramWD2, d2];
CBusWrite[ramWD3, d3];
};
 
FetchRamRD: 
PROC 
RETURNS [d0, d1, d2, d3: Byte10] ~ {
d0 ← CBusRead[ramRD0];
d1 ← CBusRead[ramRD1];
d2 ← CBusRead[ramRD2];
d3 ← CBusRead[ramRD3];
};
 
InitGPIBDevices: 
PROC ~ 
TRUSTED {
IF hardwareEnabled 
THEN {
IF ~GPIB.InitializeController[] THEN ERROR;
GPIB.InterfaceClear[];
GPIB.SelectedDeviceClear[clock8112];
GPIB.SelectedRemoteEnable[clock8112];
-- fixed edge rate, square wave output, 5V, 0V, output on
WriteGPIB[clock8112, "W1 DTY 50% HIL -0.5V LOL -2.0V D0"];
};
 
};
 
ForcePulseHigh: 
PROC [ch: ChannelIndex] = {
SetTimingChan[ch, Delay, 0, 0];
SetTimingChan[ch, Width, 0, 0];
PEWrite[ch, invCh0, 0, Delay];
PEWrite[ch, invCh2, IC[0, TRUE, FALSE].low, Delay];
PEWrite[ch, invCh0, 0, Width];
PEWrite[ch, invCh2, IC[0, FALSE, TRUE].low, Width];
};
 
ForcePulseLow: 
PROC [ch: ChannelIndex] = {
SetTimingChan[ch, Delay, 0, 0];
SetTimingChan[ch, Width, 0, 0];
PEWrite[ch, invCh0, 0, Delay];
PEWrite[ch, invCh2, IC[0, FALSE, TRUE].low, Delay];
PEWrite[ch, invCh0, 0, Width];
PEWrite[ch, invCh2, IC[0, TRUE, FALSE].low, Width];
};
 
SetDelayPassThru: 
PROC [ch: ChannelIndex] ~ {
IF gDebug THEN TerminalIO.PutF["Set Delay pass thru\n"];
SetTimingChan[ch, Delay, 0, 0];
SetTimingChan[ch, Width, 0, 0];
PEWrite[ch, invCh2, IC[0, FALSE, FALSE].low, Delay];
PEWrite[ch, invCh0, 0, Width];
PEWrite[ch, invCh2, IC[0, FALSE, TRUE].low, Width];
};
 
SetWidthPassThru: 
PROC [ch: ChannelIndex] ~ {
IF gDebug THEN TerminalIO.PutF["Set Width pass thru\n"];
SetTimingChan[ch, Delay, 0, 0];
SetTimingChan[ch, Width, 0, 0];
PEWrite[ch, invCh0, 0, Delay];
PEWrite[ch, invCh2, IC[0, FALSE, TRUE].low, Delay];
PEWrite[ch, invCh2, IC[0, FALSE, FALSE].low, Width];
};
 
SetDataPattern: 
PROC [dp: DataPattern] ~ {
Reset[];
IF gDebug THEN TerminalIO.PutF["Set pattern: %g\n", IO.rope[SELECT dp FROM p00 => "00", p01 => "01", p10 => "10", p11 => "11", ENDCASE => ERROR]];
RamWrite[CRam, 0, 03Fh, 3FFh,   0h,   0h]; --mask: FFFF, inhibit: 0000
SELECT dp 
FROM
p00 => {
RamWrite[VRam, 0,   2h,   0h,   0h,   0h]; --Lit 2,     00,     00,     00
RamWrite[VRam, 1,   0h, 080h, 080h, 080h]; --   00,Cpy 2@0,Cpy 2@0,Cpy 2@0
RamWrite[VRam, 2,   2h,   0h,   0h,   0h]; --Lit 2,     00,     00,     00
RamWrite[VRam, 3,   0h, 080h, 080h, 080h]; --   00,Cpy 2@0,Cpy 2@0,Cpy 2@0
};
 
p01 => {
RamWrite[VRam, 0,   2h,   0h,   0h,  3Fh]; --Lit 2,     00,     00,     3f
RamWrite[VRam, 1, 3FFh, 080h, 080h, 080h]; --  3FF,Cpy 2@0,Cpy 2@0,Cpy 2@0
RamWrite[VRam, 2,   2h,   0h,   0h,  3Fh]; --Lit 2,     00,     00,     3f
RamWrite[VRam, 3, 3FFh, 080h, 080h, 080h]; --  3FF,Cpy 2@0,Cpy 2@0,Cpy 2@0
};
 
p10 => {
RamWrite[VRam, 0,   2h,  3Fh, 3FFh,   0h]; --Lit 2,     3f,    3FF,     00
RamWrite[VRam, 1,   0h, 080h, 080h, 080h]; --   00,Cpy 2@0,Cpy 2@0,Cpy 2@0
RamWrite[VRam, 2,   2h,  3Fh, 3FFh,   0h]; --Lit 2,     3f,    3FF,     00
RamWrite[VRam, 3,   0h, 080h, 080h, 080h]; --   00,Cpy 2@0,Cpy 2@0,Cpy 2@0
};
 
p11 => {
RamWrite[VRam, 0,   2h,  3Fh, 3FFh,  3Fh]; --Lit 2,     3f,    3FF,     3f
RamWrite[VRam, 1, 3FFh, 080h, 080h, 080h]; --  3FF,Cpy 2@0,Cpy 2@0,Cpy 2@0
RamWrite[VRam, 2,   2h,  3Fh, 3FFh,  3Fh]; --Lit 2,     3f,    3FF,     3f
RamWrite[VRam, 3, 3FFh, 080h, 080h, 080h]; --  3FF,Cpy 2@0,Cpy 2@0,Cpy 2@0
};
 
ENDCASE => ERROR;
 
CBusWrite[writeLoopAdd, 0];
CBusWrite[writeEndAdd, 3];
StartCtl[TRUE, TRUE, FALSE];
};
 
FindOutputEdge: 
PROC [ch: ChannelIndex, p: Phase, t: Transition] 
RETURNS [time: Ns ← 0] ~ {
SetRefPhase[p];
SetRefDelayAbs[time];
WHILE 
NOT EdgeIsEarly[ch, t] 
DO
SetRefDelayAbs[(time ← time+1)];
ENDLOOP;
 
};
 
EdgeIsEarly: 
PROCEDURE [ch: ChannelIndex, t: Transition] 
RETURNS [
BOOL] = {
trueCount, falseCount: NAT ← 0;
FOR i: 
NAT 
IN [0..7) 
DO
IF (t=Down) = (Basics.
BITAND[PERead[ch, ioCtl], phaseComp]#0) 
THEN trueCount ← trueCount+1
ELSE falseCount ← falseCount + 1;
 
ENDLOOP;
 
IF gDebug THEN TerminalIO.PutF[" %g/%g ", IO.card[trueCount], IO.card[falseCount]];
RETURN[trueCount>falseCount];
};
 
SetFormat: 
PROC [ch: ChannelIndex, f: Format] = {
PEWrite[ch, format, SELECT f FROM NRZ => nrz, RZ => rz, RO => ro, RC=> rc, RT => rt, ENDCASE => ERROR];
IF gDebug THEN TerminalIO.PutF["Set format: %g\n", IO.rope[SELECT f FROM NRZ => "NRZ", RZ => "RZ", RO => "RO", RC=> "RC", RT => "RT", ENDCASE => ERROR]]
};
 
SetAnalogSample: 
PROC [ch: ChannelIndex, b: 
BOOL] = {
PEWrite[ch, ioCtl, BitOps.IBIW[b, PERead[ch, ioCtl], 2, 5]];
};
 
SetTTLLevels: 
PROC [ch: ChannelIndex, b: 
BOOL] = {
PEWrite[ch, ioCtl, BitOps.IBIW[b, PERead[ch, ioCtl], 1, 5]];
};
 
SetRefDelayAbs: 
PROC [d: Delay] ~ {
CBusWrite[refDelay, d];
IF gDebug THEN TerminalIO.PutF["Ref clock delay: %g\n", IO.card[d]];
};
 
SetRefDelay: 
PROC [d: Delay] ~ {
SetRefDelayAbs[d+gCycOffset];
};
 
SetClock: 
PROC [period: Period] ~ {
divisor: ClockDivisor;
SELECT 
TRUE 
FROM
period IN [30..70) => {divisor ← one; gClkPeriod ← period};
period IN [70..140) => {divisor ← two; gClkPeriod ← (period+1)/2};
period IN [140..280) => {divisor ← four; gClkPeriod ← (period+2)/4};
period IN [280..560) => {divisor ← eight; gClkPeriod ← (period+4)/8};
ENDCASE => ERROR;
 
SetClockDivisor[divisor];
WriteGPIB[clock8112, IO.PutFR["PER %gNS", IO.card[gClkPeriod]]];
IF gDebug THEN TerminalIO.PutF["Set clock to %g ns\n", IO.card[period]]
};
 
StartCtl: 
PROC [start, loop, reset: 
BOOL ← 
FALSE] ~ {
gClockCtl ← BitOps.IBIW[start, gClockCtl, 5, 8];
gClockCtl ← BitOps.IBIW[loop, gClockCtl, 4, 8];
gClockCtl ← BitOps.IBIW[reset, gClockCtl, 0, 8];
CBusWrite[clkCtl, gClockCtl];
};
 
SetRefPhase: 
PROC [p: Phase] ~ {
gClockCtl ← BitOps.IBIW[p=Even, gClockCtl, 2, 8];
CBusWrite[clkCtl, gClockCtl];
IF gDebug THEN TerminalIO.PutF["Set reference phase %g\n", IO.rope[IF p=Even THEN "Even" ELSE "Odd"]]
};
 
SetClockDivisor: 
PROC [cd: ClockDivisor] ~ {
gClockCtl ← BitOps.ICIW[ORD[cd], gClockCtl, 6, 2, 8];
CBusWrite[clkCtl, gClockCtl];
gClkDivisor ← cd;
};
 
PEWrite: 
PROC [chan: ChannelIndex, reg: PEReg, data: PEData, timing: Timing ← Sample] ~ {
To write the ioCtl and Format registers, use default: timing ← Sample since these regs are not asociated with any particular timing chain
CBusWrite[(chan*24)+(timing.ORD*8)+reg, data];
};
 
PERead: 
PROC [chan: ChannelIndex, reg: PEReg, timing: Timing ← Sample] 
RETURNS [data: PEData] ~ {
To read the ioCtl and Format registers, use default: timing ← Sample since these regs are not asociated with any particular timing chain
data ← Basics.BITAND[CBusRead[(chan*24)+(timing.ORD*8)+reg], 0FFh];  --PE only uses 8 bits
};
 
CBusRead: 
PROC [add: Address] 
RETURNS [data: Byte10 ← 0] ~ {
IF hardwareEnabled THEN data ← XBus.IORead[baseAdd + add*2];
};
 
CBusWrite: 
PROC [add: Address, data: Byte10] ~ {
IF hardwareEnabled THEN XBus.IOWrite[baseAdd + add*2, data];
};
 
WriteGPIB: 
PROCEDURE [device: 
GPIB.DeviceAddr, msg: Rope.
ROPE] = 
TRUSTED {
IF hardwareEnabled THEN GPIB.WriteDevice[device, Rope.Concat[msg, "\n\l"]];
};