DIRECTORY Atom, Basics, BitOps, CardTab, Convert, FS, GList, GPIB, IO, Process, Rope, RProperties, SymTab, TerminalIO, TestarossaRuntime, XBus; TestarossaRuntimeImpl: CEDAR PROGRAM IMPORTS Atom, Basics, BitOps, CardTab, Convert, FS, GList, GPIB, IO, Process, Rope, RProperties, SymTab, TerminalIO, XBus EXPORTS TestarossaRuntime = BEGIN OPEN TestarossaRuntime; Error: PUBLIC ERROR [msg: ROPE _ NIL] = CODE; ParseData: TYPE = REF ParseDataRec; ParseDataRec: TYPE = RECORD [ streams: LIST OF IO.STREAM, names: LIST OF ROPE, noticeCR: BOOL, token: ROPE, char: CHAR, kind: IO.TokenKind, charsSkipped: INT]; ParseKeywordProc: TYPE = PROC [pd: ParseData, program: Programming, nextReserved: ROPE]; Parse: PUBLIC PROC [file: ROPE] RETURNS [program: Programming] = { keywords: LIST OF ROPE _ LIST ["Parameters", "TimingGroups", "NameDutPinTimingGroup", "DutPinFixturePin", "FixturePinChipChannel", "ColumnNames", "Vectors", "End"]; keywordProcs: LIST OF ParseKeywordProc _ LIST[Parameters, TimingGroups, NameDutPinTimingGroup, DutPinFixturePin, FixturePinChipChannel, ColumnNames, Vectors]; pd: ParseData _ NEW[ParseDataRec]; pd.streams _ LIST[FS.StreamOpen[file]]; pd.names _ LIST[file]; pd.noticeCR _ FALSE; program _ NEW[ProgrammingRec]; program.timingGroups _ SymTab.Create[]; program.nameDutPinTimingGroup _ SymTab.Create[]; program.dutPinFixturePin _ CardTab.Create[]; program.fixturePinChipChannel _ CardTab.Create[]; program.columnNames _ CardTab.Create[]; program.targetAddress _ 0; ParseToken[pd]; -- prime the pump DO IF pd.kind#tokenID THEN ERROR Error["Missing keyword"]; IF NOT Rope.Equal[pd.token, keywords.first, FALSE] THEN ERROR Error["Unknown keyword"]; keywordProcs.first[pd, program, keywords.rest.first]; keywords _ keywords.rest; keywordProcs _ keywordProcs.rest; IF keywordProcs=NIL THEN EXIT; ENDLOOP; }; Parameters: ParseKeywordProc = { ParseToken[pd]; DO name: ROPE _ ParseName[pd, nextReserved]; value: REF ANY; IF name=NIL THEN EXIT; IF Rope.Fetch[pd.token] IN ['0..'9] THEN value _ NEW[INT _ Convert.IntFromRope[pd.token]] ELSE value _ pd.token; program.properties _ RProperties.PutProp[program.properties, Atom.MakeAtom[name], value]; ParseToken[pd]; ParseSeperator[pd]; ENDLOOP; }; TimingGroups: ParseKeywordProc = { ParseToken[pd]; DO name: ROPE _ NIL; group: TimingGroup _ NEW[TimingGroupRec]; IF (name _ ParseName[pd, nextReserved])=NIL THEN EXIT; IF pd.kind#tokenID THEN ERROR Error["Expecting timing group format"]; group.format _ SELECT TRUE FROM Rope.Equal[pd.token, "NRZ", FALSE] => NRZ, Rope.Equal[pd.token, "RZ", FALSE] => RZ, Rope.Equal[pd.token, "RO", FALSE] => RO, Rope.Equal[pd.token, "RC", FALSE] => RC, Rope.Equal[pd.token, "RT", FALSE] => RT, ENDCASE => ERROR Error["Unknown timing group format"]; ParseToken[pd]; group.delay _ ParseNs[pd]; group.width _ ParseNs[pd]; group.sample _ ParseNs[pd]; group.variableThreshold _ ParseBool[pd]; group.variableLevels _ ParseBool[pd]; ParseSeperator[pd]; IF NOT SymTab.Store[program.timingGroups, name, group] THEN ERROR Error["Multiply defined timing group"]; ENDLOOP; }; NameDutPinTimingGroup: ParseKeywordProc = { ParseToken[pd]; DO name: ROPE _ NIL; group: DutPinTimingGroup _ NEW[DutPinTimingGroupRec]; groups: DutPinTimingGroups; IF (name _ ParseName[pd, nextReserved])=NIL THEN EXIT; IF pd.kind=tokenSINGLE THEN { IF pd.char#'[ THEN ERROR Error["Bad bus index"]; ParseToken[pd]; group.index _ ParseNumber[pd]; IF pd.char#'] THEN ERROR Error["Bad bus index"]; ParseToken[pd]; } ELSE group.index _ 0; group.dutPin _ ParseNumber[pd]; group.timingGroup _ ParseName[pd]; ParseSeperator[pd]; groups _ NARROW[SymTab.Fetch[program.nameDutPinTimingGroup, name].val]; FOR s: DutPinTimingGroups _ groups, s.rest UNTIL s=NIL DO IF s.first.index=group.index THEN ERROR Error["Multiply defined signal"]; ENDLOOP; groups _ CONS[group, groups]; [] _ SymTab.Store[program.nameDutPinTimingGroup, name, groups]; ENDLOOP; }; DutPinFixturePin: ParseKeywordProc = { ParseToken[pd]; DO dutPin: CARD _ 0; fixturePin: REF CARD _ NEW[CARD]; IF pd.kind#tokenDECIMAL THEN EXIT; dutPin _ ParseNumber[pd]; fixturePin^ _ ParseNumber[pd]; ParseSeperator[pd]; IF NOT CardTab.Store[program.dutPinFixturePin, dutPin, fixturePin] THEN ERROR Error["Multiply defined Dut Pin"]; ENDLOOP; }; FixturePinChipChannel: ParseKeywordProc = { ParseToken[pd]; DO fixturePin: CARD _ 0; chipChannel: ChannelAddress _ NEW[ChannelAddressRec]; IF pd.kind#tokenDECIMAL THEN EXIT; fixturePin _ ParseNumber[pd]; chipChannel.chip _ ParseNumber[pd]; chipChannel.channel _ ParseNumber[pd]; ParseSeperator[pd]; IF NOT CardTab.Store[program.fixturePinChipChannel, fixturePin, chipChannel] THEN ERROR Error["Multiply defined Fixture Pin"]; ENDLOOP; }; ColumnNames: ParseKeywordProc = { position: CARD _ 0; pd.noticeCR _ TRUE; ParseToken[pd]; DO SELECT pd.kind FROM tokenSINGLE => { IF pd.char#IO.CR THEN ERROR Error["Expecting carriage return in ColumnNames"]; position _ 0; ParseToken[pd]; }; tokenID => { name: ROPE; IF Rope.Equal[pd.token, nextReserved, FALSE] THEN EXIT; IF Rope.Length[pd.token]#1 THEN ERROR Error["Expecting only one character in ColumnNames"]; position _ position + pd.charsSkipped + 1; name _ NARROW[CardTab.Fetch[program.columnNames, position].val]; name _ Rope.Concat[name, Rope.FromChar[pd.char]]; [] _ CardTab.Store[program.columnNames, position, name]; ParseToken[pd]; }; ENDCASE => ERROR Error["Expecting character in ColumnNames"]; ENDLOOP; pd.noticeCR _ FALSE; }; Vectors: ParseKeywordProc = { ConsVector: PROC = { IF triples#NIL THEN { triplesLength: CARD _ GList.Length[triples]; vectors _ CONS[NEW[ParsedVectorRec[triplesLength]], vectors]; FOR index: CARD DECREASING IN [0..triplesLength) DO vectors.first[index] _ triples.first; triples _ triples.rest; ENDLOOP; vectorLength _ vectorLength + 1; }; }; vectors: LIST OF ParsedVector _ NIL; triples: LIST OF ParsedVectorTriple _ NIL; position: CARD _ 0; vectorLength: CARD _ 0; pd.noticeCR _ TRUE; ParseToken[pd]; DO SELECT pd.kind FROM tokenSINGLE => { SELECT pd.char FROM IO.CR => { position _ 1; ParseToken[pd]; ConsVector[]; }; '* => { position _ position + pd.charsSkipped + 1; ParseToken[pd]; program.targetAddress _ vectorLength; }; ENDCASE => ERROR Error["Expecting carriage return in Vectors"]; }; tokenDECIMAL, tokenID => { triple: ParsedVectorTriple; skip: CARD; size: CARD; IF Rope.Equal[pd.token, nextReserved, FALSE] THEN EXIT; triple _ NEW[ParsedVectorTripleRec]; skip _ pd.charsSkipped + Rope.Length[pd.token]; size _ VectorColumnToBusSize[program, position + pd.charsSkipped]; triple.value _ ConvertBoolSequence[pd.token, size]; ParseToken[pd]; position _ position + skip; [triple.inhibit, skip] _ ParseBoolSequence[pd, size]; position _ position + skip; [triple.mask, skip] _ ParseBoolSequence[pd, size]; position _ position + skip; triples _ CONS[triple, triples]; }; ENDCASE => ERROR Error["Unknown token type in Vectors"]; ENDLOOP; pd.noticeCR _ FALSE; ConsVector[]; program.vectors _ NEW[ParsedVectorsRec[vectorLength]]; FOR index: CARD DECREASING IN [0..vectorLength) DO program.vectors[index] _ vectors.first; vectors _ vectors.rest; ENDLOOP; }; VectorColumnToBusSize: PROC [program: Programming, column: CARD] RETURNS [size: CARD] = { name: ROPE _ NARROW[CardTab.Fetch[program.columnNames, column].val]; groups: DutPinTimingGroups; max: CARD _ 0; min: CARD _ LAST[CARD]; IF name=NIL THEN ERROR Error["Some column is not properly named"]; groups _ NARROW[SymTab.Fetch[program.nameDutPinTimingGroup, name].val]; IF groups=NIL THEN ERROR Error["Some signal has vectors but is not defined in NameDutPinTimingGroup"]; UNTIL groups=NIL DO max _ MAX[groups.first.index, max]; min _ MIN[groups.first.index, min]; groups _ groups.rest; ENDLOOP; size _ max-min+1; }; ParseBool: PROC [pd: ParseData] RETURNS [bool: BOOL] = { IF pd.kind#tokenID THEN ERROR Error["Expecting boolean"]; IF Rope.Length[pd.token]#1 THEN ERROR Error["Expecting only T or F for boolean"]; bool _ SELECT pd.char FROM 'T, 't => TRUE, 'F, 'f => FALSE, ENDCASE => ERROR Error["Expecting only T or F for boolean"]; ParseToken[pd]; }; ParseBoolSequence: PROC [pd: ParseData, size: CARD] RETURNS [bools: BoolSequence, skip: CARD] = { IF NOT (pd.kind=tokenDECIMAL OR pd.kind=tokenID) THEN ERROR Error["Expecting hexadecimal number in Vectors"]; bools _ ConvertBoolSequence[pd.token, size]; ParseToken[pd]; skip _ pd.charsSkipped + Rope.Length[pd.token]; }; ConvertBoolSequence: PROC [token: ROPE, size: CARD] RETURNS [bools: BoolSequence] = { boolIndex: CARD _ size; bools _ NEW[BoolSequenceRec[size]]; FOR index: CARD DECREASING IN [0..LOOPHOLE[Rope.Length[token]]) DO char: CHAR _ Rope.Fetch[token, index]; charValue: CARD _ SELECT TRUE FROM char IN ['0..'9] => char-'0, char IN ['a..'f] => char-'a+10, char IN ['A..'F] => char-'A+10, ENDCASE => ERROR Error["Bad hexadecimal digit in Vectors"]; THROUGH [1..4] DO boolIndex _ boolIndex - 1; bools[boolIndex] _ IF charValue MOD 2=1 THEN TRUE ELSE FALSE; IF boolIndex=0 THEN EXIT; charValue _ charValue / 2; ENDLOOP; IF boolIndex=0 AND index#0 THEN ERROR Error["Too many digits"]; ENDLOOP; }; ParseName: PROC [pd: ParseData, nextReserved: ROPE _ NIL] RETURNS [name: ROPE _ NIL] = { IF pd.kind#tokenID THEN ERROR Error["Expecting name or keyword"]; IF nextReserved#NIL AND Rope.Equal[pd.token, nextReserved, FALSE] THEN RETURN; name _ pd.token; ParseToken[pd]; }; ParseNumber, ParseNs: PROC [pd: ParseData] RETURNS [ns: Ns] = { IF pd.kind#tokenDECIMAL THEN ERROR Error["Expecting number"]; ns _ Convert.CardFromRope[pd.token]; ParseToken[pd]; }; ParseSeperator: PROC [pd: ParseData] = { IF pd.kind#tokenSINGLE THEN ERROR Error["Expecting seperator"]; IF pd.char#'; THEN ERROR Error["Bad seperator"]; ParseToken[pd]; }; ParseToken: PROC [pd: ParseData] = { DO { ParseOneToken[pd ! IO.EndOfStream => GOTO PopFile]; EXIT; EXITS PopFile => { IO.Close[pd.streams.first]; pd.streams _ pd.streams.rest; pd.names _ pd.names.rest; IF pd.streams=NIL THEN ERROR; -- more pops than includes? LOOP; }; }; ENDLOOP; }; ParseOneToken: PROC [pd: ParseData] = { ParseOnlyOneToken[pd]; IF pd.kind=tokenID AND Rope.Equal[pd.token, "Include", FALSE] THEN { file: ROPE; [] _ IO.SkipWhitespace[pd.streams.first ! IO.EndOfStream => GOTO BadInclude]; file _ IO.GetLineRope[pd.streams.first ! IO.EndOfStream => GOTO BadInclude]; pd.streams _ CONS[FS.StreamOpen[file], pd.streams]; pd.names _ CONS[file, pd.names]; ParseOnlyOneToken[pd]; EXITS BadInclude => ERROR Error["No file name for include"]; }; }; ParseOnlyOneToken: PROC [pd: ParseData] = { [pd.token, pd.charsSkipped] _ IO.GetTokenRope[pd.streams.first, IF pd.noticeCR THEN BreaksCR ELSE Breaks]; pd.char _ Rope.Fetch[pd.token]; pd.kind _ SELECT TRUE FROM Rope.Length[pd.token]=1 AND (IF pd.noticeCR THEN BreaksCR ELSE Breaks)[pd.char]=break => tokenSINGLE, pd.char IN ['0..'9] => tokenDECIMAL, ENDCASE => tokenID; }; BreaksCR: IO.BreakProc = { RETURN[SELECT char FROM IO.CR => break, IN [IO.NUL .. IO.SP] => sepr, '*, '[, '], ';, '. => break, IN ['0..'9], IN ['a..'z], IN ['A..'Z] => other, ENDCASE => ERROR Error["Illegal character"]]; }; Breaks: IO.BreakProc = { RETURN[SELECT char FROM IN [IO.NUL .. IO.SP] => sepr, '[, '], ';, '. => break, IN ['0..'9], IN ['a..'z], IN ['A..'Z] => other, ENDCASE => ERROR Error["Illegal character"]]; }; debug: BOOL _ TRUE; minCopyLength: CARDINAL = 3; minCopyLengthNoLiteral: CARDINAL = 2; maxCopyLength: CARDINAL = 15; maxLiteralLength: CARDINAL = 64; bufferSize: CARD = 64; BufferIndex: TYPE = [0..bufferSize); DoubleByte: TYPE = RECORD [first, second: Byte10]; ColumnNameList: TYPE = LIST OF ColumnName; ColumnName: TYPE = REF ColumnNameRec; ColumnNameRec: TYPE = RECORD [ column: CARD, name: ROPE]; TripleSequence: TYPE = REF TripleSequenceRec; TripleSequenceRec: TYPE = RECORD [elements: SEQUENCE size: CARDINAL OF ChipTripleSequence]; -- chip ChipTripleSequence: TYPE = ARRAY ChannelIndex OF TripleSequenceIndex; TripleSequenceIndex: TYPE = RECORD [ assigned: BOOL, triple: CARDINAL, sequence: CARDINAL]; Transform: PUBLIC PROC [program: Programming] RETURNS [fixture: Fixture] = { FindMaxChip: CardTab.EachPairAction = { ca: ChannelAddress _ NARROW[val]; chips _ MAX[ca.chip+1, chips]; }; BuildColumnNames: CardTab.EachPairAction = { columnNames _ CONS[NEW[ColumnNameRec], columnNames]; columnNames.first.column _ key; columnNames.first.name _ NARROW[val]; }; CompareColumns: GList.CompareProc = { first: ColumnName _ NARROW[ref1]; second: ColumnName _ NARROW[ref2]; RETURN[Basics.CompareCard[first.column, second.column]]; }; FillFormatTiming: SymTab.EachPairAction = { FOR groups: DutPinTimingGroups _ NARROW[val], groups.rest UNTIL groups=NIL DO group: DutPinTimingGroup _ groups.first; fixturePin: REF CARD _ NARROW[CardTab.Fetch[program.dutPinFixturePin, group.dutPin].val]; chipChannel: ChannelAddress; timingGroup: TimingGroup; triplet: CARD _ 0; IF fixturePin=NIL THEN ERROR Error["No fixture pin assigned for dut pin"]; chipChannel _ NARROW[CardTab.Fetch[program.fixturePinChipChannel, fixturePin^].val]; IF chipChannel=NIL THEN ERROR Error["No chip and channel assigned for fixture pin"]; timingGroup _ NARROW[SymTab.Fetch[program.timingGroups, group.timingGroup].val]; IF timingGroup=NIL THEN ERROR; fixture[chipChannel.chip].formatTiming[chipChannel.channel] _ timingGroup; FOR names: ColumnNameList _ columnNames, names.rest UNTIL names=NIL DO IF Rope.Equal[names.first.name, key] THEN EXIT; triplet _ triplet + 1; REPEAT FINISHED => ERROR; ENDLOOP; map[chipChannel.chip][chipChannel.channel] _ [TRUE, triplet, group.index]; ENDLOOP; }; chips: CARD _ 0; map: TripleSequence; columnNames: ColumnNameList _ NIL; [] _ CardTab.Pairs[program.fixturePinChipChannel, FindMaxChip]; fixture _ AllocateFixture[chips]; fixture.properties _ program.properties; map _ NEW[TripleSequenceRec[chips]]; FOR chipIndex: CARD IN [0..chips) DO FOR channelIndex: CARD IN [0..channelsPerChip) DO map[chipIndex][channelIndex] _ [FALSE, 0, 0]; ENDLOOP; ENDLOOP; [] _ CardTab.Pairs[program.columnNames, BuildColumnNames]; columnNames _ NARROW[GList.Sort[columnNames, CompareColumns]]; [] _ SymTab.Pairs[program.nameDutPinTimingGroup, FillFormatTiming]; FOR chipIndex: CARD IN [0..chips) DO IF debug THEN TerminalIO.PutF["\nchip: %g", IO.int[chipIndex]]; FillMaskInhibitVector[program, fixture[chipIndex], map[chipIndex]]; ENDLOOP; }; AllocateFixture: PROC [chips: CARD] RETURNS [fixture: Fixture] = { fixture _ NEW[FixtureRec[chips]]; FOR chipIndex: CARD IN [0..chips) DO chip: Chip _ NEW[ChipRec[vectorAddressesPerChip]]; fixture[chipIndex] _ chip; FOR channel: ChannelIndex IN ChannelIndex DO chip.formatTiming[channel] _ NIL; FOR imIndex: InhibitMaskIndex IN InhibitMaskIndex DO chip.inhibitMask[imIndex][channel].inhibit _ FALSE; chip.inhibitMask[imIndex][channel].mask _ FALSE; ENDLOOP; ENDLOOP; chip.targetAddress _ 0; chip.matchAddress _ vectorAddressesPerChip; chip.firstFreeVector _ 0; FOR vectorIndex: CARD IN [0..vectorAddressesPerChip) DO FOR byteIndex: ByteIndex IN ByteIndex DO chip.vectors[vectorIndex][byteIndex] _ 0; ENDLOOP; ENDLOOP; ENDLOOP; }; FillMaskInhibitVector: PROC [program: Programming, chip: Chip, map: ChipTripleSequence] = { LiteralLength: PROC RETURNS [length: NAT] = { length _ LOOPHOLE[nextInsert-copyLength-literalPosition, CARDINAL] MOD bufferSize; }; EmitLiteral: PROC [length: NAT] = { IF length>0 THEN { IF debug THEN TerminalIO.PutF["\nLit(%g):", IO.card[length]]; EmitByte10[IF length=maxLiteralLength THEN 0 ELSE length]; FOR index: NAT IN [0..length) DO vector: BitOps.BitDWord _ buffer[literalPosition+index]; IF debug THEN TerminalIO.PutF[" %x", IO.int[LOOPHOLE[vector]]]; EmitByte10[BitOps.ECFD[vector, 0, 10, 20]]; EmitByte10[BitOps.ECFD[vector, 10, 10, 20]]; ENDLOOP; literalPosition _ (literalPosition+length) MOD bufferSize }; }; EmitCopy: PROC = { IF copyLength>0 THEN { position: NAT _ LOOPHOLE[matchPosition+1-copyLength, CARDINAL] MOD bufferSize; IF debug THEN TerminalIO.PutF["\nCopy l:%g, p: %g", IO.card[copyLength], IO.card[position]]; EmitByte10[BitOps.WordOR[BitOps.WordShift[copyLength, 6], position]]; copyLength _ 0; literalPosition _ nextInsert; }; }; EmitByte10: PROC [byte: Byte10] = { IF chip.firstFreeVector=vectorAddressesPerChip AND firstFreeByte=bytesPerAddress THEN ERROR Error["Not enough storage for this vector set"]; chip.vectors[chip.firstFreeVector][firstFreeByte] _ byte; firstFreeByte _ firstFreeByte + 1; IF firstFreeByte=bytesPerAddress THEN { firstFreeByte _ 0; chip.firstFreeVector _ chip.firstFreeVector + 1; }; }; AlignCodeWords: PROC [dontModifyBefore: CARDINAL] = { PushDown: PROC [byteCount: CARDINAL] = { IF byteCount>0 THEN { getByte: ByteIndex _ firstFreeByte; getWord: BufferIndex _ chip.firstFreeVector; putByte: ByteIndex _ (firstFreeByte + byteCount) MOD bytesPerAddress; putWord: BufferIndex _ chip.firstFreeVector + ((byteCount + bytesPerAddress - 1) / bytesPerAddress); IF putWord>vectorAddressesPerChip OR (putWord=vectorAddressesPerChip AND putByte>0) THEN ERROR; -- should never happen since padding an existing word firstFreeByte _ putByte; chip.firstFreeVector _ putWord; UNTIL getByte=getByteIndex AND getWord=getWordIndex DO IF getByte=0 THEN { getByte _ bytesPerAddress - 1; getWord _ getWord - 1; } ELSE getByte _ getByte - 1; IF putByte=0 THEN { putByte _ bytesPerAddress - 1; putWord _ putWord - 1; } ELSE putByte _ putByte - 1; chip.vectors[putWord][putByte] _ chip.vectors[getWord][getByte]; ENDLOOP; getByteIndex _ putByte; getWordIndex _ putWord; }; }; GetByte: PROC RETURNS [byte: Byte10] = { IF getWordIndex=chip.firstFreeVector AND getByteIndex=firstFreeByte THEN ERROR Error["Couldn't align code words"]; byte _ chip.vectors[getWordIndex][getByteIndex]; getByteIndex _ (getByteIndex + 1) MOD bytesPerAddress; IF getByteIndex=0 THEN getWordIndex _ getWordIndex + 1; }; PutByte: PROC [byte: Byte10] = { chip.vectors[putWordIndex][putByteIndex] _ byte; putByteIndex _ (putByteIndex + 1) MOD bytesPerAddress; IF putByteIndex=0 THEN putWordIndex _ putWordIndex + 1; }; putWordIndex: BufferIndex _ dontModifyBefore; getWordIndex: BufferIndex _ dontModifyBefore; putByteIndex: ByteIndex _ 0; getByteIndex: ByteIndex _ 0; WHILE firstFreeByte>0 DO byte: Byte10; putByteIndex _ getByteIndex; putWordIndex _ getWordIndex; byte _ GetByte[]; IF byte { IF firstFreeInhibitMask=inhibitMaskPerChip THEN ERROR Error["Not enough inhibit/mask entries for this vector set"]; chip.inhibitMask[firstFreeInhibitMask] _ inhibitMask; inhibitMaskIndex _ firstFreeInhibitMask; firstFreeInhibitMask _ firstFreeInhibitMask + 1; }; ENDLOOP; value _ BitOps.DoubleOR[value, BitOps.DoubleShift[inhibitMaskIndex, 16]]; IF debug THEN TerminalIO.PutF["\nvalue: %x", IO.int[value]]; SELECT TRUE FROM copyLength=0 => IF Match[TRUE] THEN copyLength _ 1; Match[FALSE] => copyLength _ copyLength+1; copyLength>=minCopyLength => { EmitLiteral[LiteralLength[]]; EmitCopy[]; IF Match[TRUE] THEN copyLength _ 1; }; LiteralLength[]=0 AND copyLength>=minCopyLengthNoLiteral => { EmitCopy[]; IF Match[TRUE] THEN copyLength _ 1; }; ENDCASE => { copyLength _ 0; IF LiteralLength[]>=maxLiteralLength THEN EmitLiteral[maxLiteralLength]; }; buffer[nextInsert] _ value; nextInsert _ (nextInsert + 1) MOD bufferSize; { emitCopy: BOOL _ copyLength=maxCopyLength; length: NAT _ LiteralLength[]; IF emitCopy OR length=maxLiteralLength THEN EmitLiteral[length]; IF emitCopy THEN EmitCopy[]; }; ENDLOOP; IF copyLength < minCopyLength THEN { litLen: NAT; copyLength _ 0; litLen _ LiteralLength[]; IF litLen # 0 THEN EmitLiteral[litLen]; } ELSE EmitCopy[]; AlignCodeWords[chip.targetAddress]; chip.matchAddress _ chip.firstFreeVector - 1; }; PrintFixture: PUBLIC PROC [fixture: Fixture] = { FOR chipIndex: CARDINAL IN [0..fixture.size) DO GetByte: PROC RETURNS [byte: Byte10] = { byte _ chip.vectors[vectorIndex][byteIndex]; byteIndex _ byteIndex + 1; IF byteIndex=bytesPerAddress THEN { byteIndex _ 0; vectorIndex _ vectorIndex + 1; } }; chip: Chip _ fixture[chipIndex]; vectorIndex: CARDINAL _ 0; byteIndex: CARDINAL _ 0; TerminalIO.PutF["\nchip: %g", IO.card[chipIndex]]; TerminalIO.PutF["\nfirstFreeVector: %g", IO.card[chip.firstFreeVector]]; TerminalIO.PutF["\nInhibitMask"]; FOR inhibitMaskIndex: InhibitMaskIndex IN InhibitMaskIndex DO TerminalIO.PutF["\n%g:", IO.card[inhibitMaskIndex]]; FOR channelIndex: ChannelIndex IN ChannelIndex DO TerminalIO.PutF[" (%g, %g)", IO.card[IF chip.inhibitMask[inhibitMaskIndex][channelIndex].inhibit THEN 1 ELSE 0], IO.card[IF chip.inhibitMask[inhibitMaskIndex][channelIndex].mask THEN 1 ELSE 0]]; ENDLOOP; ENDLOOP; UNTIL vectorIndex>=chip.firstFreeVector DO byte: Byte10 _ GetByte[]; IF byte=chip.firstFreeVector DO byte: Byte10 _ GetByte[]; IF byte 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 ~ { 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 RETURN[hc0, is0]; hc0=hc1 AND is0>is1 => RETURN[hc1, is1]; hc0=hc1 AND is0 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]; 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] ~ { CBusWrite[(chan*24)+(timing.ORD*8)+reg, data]; }; PERead: PROC [chan: ChannelIndex, reg: PEReg, timing: Timing _ Sample] RETURNS [data: PEData] ~ { 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"]]; }; END. „TestarossaRuntimeImpl.mesa Copyright ำ 1985, 1987 by Xerox Corporation. All rights reserved. Barth, October 18, 1988 11:57:28 am PDT Parse Format Transform Pickle Execute Types Constants Globals CBus Addresses Pin Electronics Each DUT pin Each Timing Generator (Sample, Width, Delay) Decompressor Ref Clock Generator (on Multibus card) ioCtl Register Bits format Register Bits writeExtRamCtl Register Bits readDebug Register Bits Procedures None of the routines are parameterized by the chip number! 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. -- fixed edge rate, square wave output, 5V, 0V, output on To write the ioCtl and Format registers, use default: timing _ Sample since these regs are not asociated with any particular timing chain To read the ioCtl and Format registers, use default: timing _ Sample since these regs are not asociated with any particular timing chain ส:ภ– "cedar" style˜codešœ™KšœB™BKšœ'™'K™—Kšฯk œ)œ œJ˜K˜•StartOfExpansion[]šฯnœœ˜$Kšœ)œ œ6˜yKšœ˜Kšœœ˜—K˜Kš žœœœœœœ˜-headšœ™Kšœ œœ˜#šœœœ˜Kš œ œœœœ˜Kšœœœœ˜Kšœ œ˜Kšœœ˜ Kšœœ˜ Kšœœ ˜Kšœœ˜K˜—šœœœ5œ˜XK˜—š žœœœœœ˜BKš œ œœœœ‡˜คKšœœœœq˜žKšœœ˜"Kšœ œœ˜'Kšœ œ˜Kšœœ˜Kšœ œ˜Kšœ'˜'Kšœ0˜0Kšœ,˜,Kšœ1˜1Kšœ'˜'Kšœ˜Kšœฯc˜"š˜Kšœœœ˜7Kš œœ&œœœ˜WKšœ5˜5Kšœ˜Kšœ!˜!Kšœœœœ˜Kšœ˜—K˜K˜—šž œ˜ Kšœ˜š˜Kšœœ˜)Kšœœœ˜Kšœœœœ˜Kš œœ œ œœ˜YKšœ˜KšœY˜YK˜Kšœ˜Kšœ˜—Kšœ˜K˜—šž œ˜"Kšœ˜š˜Kšœœœ˜Kšœœ˜)Kšœ&œœœ˜6™Kšœœœ(˜Ešœœœ˜Kšœœœ˜*Kšœœœ˜(Kšœœœ˜(Kšœœœ˜(Kšœœœ˜(Kšœœ&˜6—Kšœ˜—Kšœ˜Kšœ˜Kšœ˜Kšœ(˜(Kšœ%˜%Kšœ˜Kšœœ1œœ(˜iKšœ˜—Kšœ˜K˜—šžœ˜+Kšœ˜š˜Kšœœœ˜Kšœœ˜5Kšœ˜Kšœ&œœœ˜6šœœ˜Kšœ œœ˜0K˜Kšœ˜Kšœ œœ˜0K˜K˜—Kšœ˜Kšœ˜Kšœ"˜"Kšœ˜Kšœ œ8˜Gšœ(œœ˜9Kšœœœ"˜IKšœ˜—Kšœ œ˜Kšœ?˜?Kšœ˜—Kšœ˜K˜—šžœ˜&Kšœ˜š˜Kšœœ˜Kš œ œœœœ˜!Kšœœœ˜"Kšœ˜Kšœ˜Kšœ˜Kšœœ=œœ#˜pKšœ˜—Kšœ˜K˜—šžœ˜+Kšœ˜š˜Kšœ œ˜Kšœœ˜5Kšœœœ˜"Kšœ˜Kšœ#˜#Kšœ&˜&Kšœ˜KšœœGœœ'˜~Kšœ˜—Kšœ˜K˜—šž œ˜!Kšœ œ˜Kšœœ˜Kšœ˜š˜šœ ˜šœ˜Kš œ œœœœ3˜NKšœ ˜ Kšœ˜K˜—šœ ˜ Kšœœ˜ Kšœ$œœœ˜7Kšœœœ6˜[Kšœ*˜*Kšœœ3˜@K˜1Kšœ8˜8Kšœ˜K˜—Kšœœ-˜=—Kšœ˜—Kšœœ˜Kšœ˜K˜—šžœ˜šž œœ˜šœ œœ˜Kšœœ˜,Kšœ œœ+˜=š œœ œœ˜3Kšœ%˜%Kšœ˜Kšœ˜—Kšœ ˜ K˜—K˜—Kšœ œœœ˜$Kšœ œœœ˜*Kšœ œ˜Kšœœ˜Kšœœ˜Kšœ˜š˜šœ ˜šœ˜šœ ˜šœœ˜ Kšœ ˜ Kšœ˜Kšœ ˜ K˜—˜Kšœ*˜*Kšœ˜Kšœ%˜%K˜—Kšœœ/˜?—K˜—šœ˜Kšœ˜Kšœœ˜ Kšœœ˜ Kšœ$œœœ˜7Kšœ œ˜$Kšœ/˜/KšœB˜BKšœ3˜3Kšœ˜Kšœ˜Kšœ5˜5Kšœ˜Kšœ2˜2Kšœ˜Kšœ œ˜ K˜—Kšœœ(˜8—Kšœ˜—Kšœœ˜Kšœ ˜ Kšœœ!˜6š œœ œœ˜2Kšœ'˜'Kšœ˜Kšœ˜—šœ˜K˜——š ะbnœœ œœœ˜YKšœœœ1˜DKšœ˜Kšœœ˜Kšœœœœ˜Kšœœœœ,˜BKšœ œ8˜GKšœœœœN˜fšœœ˜Kšœœ˜#Kšœœ˜#Kšœ˜Kšœ˜—K˜K˜K˜—šž œœœœ˜8Kšœœœ˜9Kšœœœ,˜Qšœœ ˜Kšœ œ˜Kšœ œ˜Kšœœ,˜<—Kšœ˜Kšœ˜K˜—š žœœœœœ˜aKš œœœœœ2˜mKšœ,˜,Kšœ˜Kšœ/˜/Kšœ˜K˜—š žœœ œœœ˜UKšœ œ˜Kšœœ˜#š œœ œœœ˜BKšœœ˜&šœ œœœ˜"Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ+˜;—šœ˜Kšœ˜Kš œœ œœœœœ˜=Kšœ œœ˜Kšœ˜Kšœ˜—Kšœ œ œœ˜?Kšœ˜—Kšœ˜K˜—šž œœœœœœœ˜XKšœœœ$˜AKš œœœ$œœœ˜NK˜Kšœ˜Kšœ˜K˜—šž œžœœœ ˜?Kšœœœ˜=Kšœ$˜$Kšœ˜Kšœ˜K˜—šžœœ˜(Kšœœœ˜?Kšœ œœ˜0Kšœ˜Kšœ˜K˜—šž œœ˜$š˜šœ˜Kšœœœ ˜3Kšœ˜šœ ˜Kšœ˜Kšœ˜Kšœ˜Kš œ œœœŸ˜:Kšœ˜K˜—K˜—Kšœ˜—Kšœ˜K˜—šž œœ˜'Kšœ˜šœœ!œœ˜DKšœœ˜ Kšœœ#œœ ˜MKšœœ œœ ˜LKšœ œœ˜3Kšœ œ˜ Kšœ˜Kšœœ#˜KšœC˜Cšœ œœ ˜$Jšœœœ˜?KšœC˜CKšœ˜—K˜K˜—šžœœ œœ˜BKšœ œ˜!šœ œœ ˜$Kšœ œ"˜2Kšœ˜šœœ˜,Kšœœ˜!šœœ˜4Kšœ-œ˜3Kšœ*œ˜0Kšœ˜—Kšœ˜—Kšœ˜Kšœ+˜+Kšœ˜šœœœ˜7šœœ ˜(Kšœ)˜)Kšœ˜—Kšœ˜—Kšœ˜—K˜K˜—šžœœ@˜[šž œœœ œ˜-Kšœ œ(œœ ˜RKšœ˜—šž œœ œ˜#šœ œ˜Kšœœœ˜=Kšœ œœœ ˜:šœœœ ˜ Kšœ8˜8Kšœœœœ ˜?Kšœœ˜+Kšœœ˜,Kšœ˜—Kšœ+œ ˜9K˜—Kšœ˜—šžœœ˜šœœ˜Kš œ œœœœ ˜NJšœœ'œœ˜\JšœE˜EKšœ˜Kšœ˜K˜—Kšœ˜—šž œœ˜#Kšœ-œœœ1˜ŒKšœ9˜9Kšœ"˜"šœœ˜'Kšœ˜Kšœ0˜0K˜—Kšœ˜—šžœœœ˜5šžœœ œ˜(šœ œ˜Kšœ#˜#Kšœ,˜,Kšœ1œ˜EKšœd˜dKš œ œ!œ œœŸ5˜–Kšœ˜Kšœ˜šœœ˜6šœ œ˜Kšœ˜Kšœ˜K˜—Kšœ˜šœ œ˜Kšœ˜Kšœ˜K˜—Kšœ˜Kšœ@˜@Kšœ˜—Kšœ˜Kšœ˜K˜—K˜—šžœœœ˜(Kšœ#œœœ$˜rKšœ0˜0Kšœ"œ˜6Kšœœ!˜7K˜—šžœœ˜ Kšœ0˜0Kšœ"œ˜6Kšœœ!˜7K˜—Kšœ-˜-Kšœ-˜-Kšœ˜Kšœ˜šœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜šœœ˜Kšœ œ˜Kšœœ˜'Kšœ œ(˜8Kšœ˜šœ˜Kšœ Ÿ˜KšœŸ˜)KšœŸ˜*Kšœ˜—KšœŸ˜6K˜—šœ˜Kšœœ œ˜/Kšœ œ œ˜1Kšœ œœ*˜DKšœ˜šœ˜Kšœœœ˜FKšœœ ˜)Kšœ˜—Kšœœœ/Ÿ˜pKšœ˜—Kšœ˜—Kšœ˜—Kšœœ˜#Kšœœ˜Kšœ#˜#Kšœœ œœ˜HKš œ œ œœœœ˜2Kšœœ˜Kšœœ˜Kšœ œ˜Kšœ œ˜šœœœ˜/š žœœ œœ œœ˜˜sKšœ5˜5Kšœ(˜(Kšœ0˜0K˜—Kšœ˜—KšœI˜IJšœœ œ ˜<šœœ˜Kšœœœœ˜3Kšœœ˜*šœ˜Kšœ˜K˜ Kšœœœ˜#Kšœ˜—šœœ(˜=K˜ Kšœœœ˜#Kšœ˜—šœ˜ Kšœ˜Kšœ#œ˜HK˜——Kšœ˜Kšœœ ˜-šœ˜Kšœ œ˜*Kšœœ˜Kšœ œœ˜@Kšœ œ ˜K˜—Kšœ˜—šœœ˜$Kšœœ˜ Kšœ˜Kšœ˜Kšœ œ˜'Kšœ˜—Kšœ ˜Kšœ#˜#Kšœ-˜-K˜K˜—šž œœœ˜0šœ œœ˜/šžœœœ˜(Kšœ,˜,Kšœ˜šœœ˜#Kšœ˜Kšœ˜K˜—K˜—Kšœ ˜ Kšœ œ˜Kšœ œ˜Kšœœ˜2Kšœ)œ˜HKšœ!˜!šœ$œ˜=Kšœœ˜4šœœ˜1Kšœœœ:œœœœ7œœ˜ยKšœ˜—Kšœ˜—šœ#˜*Kšœ˜šœœ˜Kšœœ˜'Kšœœ ˜-šœ ˜Kšœ˜K˜#K˜-Kšœœ˜'Kšœ˜—K˜—Kš œ'œ œœ œ˜wKšœ˜—Kšœ˜—K˜K˜—šžœœœ;œ ˜lšžœœœ˜(Kšœ,˜,Kšœ˜šœœ˜#Kšœ˜Kšœ˜K˜—K˜—šž œœ œ˜#Kšœ#˜#Kšœœ˜ Kšœœ˜0Kšœ œ˜0Kšœœ œœ:œœœœ7œœ˜าK˜—Kšœ ˜ Kšœ œ˜Kšœ œ˜Kšœœ œœ˜HKšœ œ˜šœ#˜*Kšœ˜šœœ˜Kšœœ˜'šœ ˜Kšœ˜K˜#K˜-Kšœ˜Kšœœ ˜-K˜Kšœ˜—K˜—šœ˜Kšœœ œ˜/Kšœ œ œ˜1šœ ˜Kšœ˜Kšœ&˜&Kšœœ ˜)Kšœœ ˜-Kšœ˜—K˜—Kšœ˜—K˜K˜——šœ™šžœœœœ˜6K˜K˜—š žœœœœœ˜AK˜K˜——™™Kšœœ˜&Kšœœ ˜Kšœœ ˜Kšœœ˜&Kšœ œ ˜Kšœ œ ˜Kšœ œ ˜Kšœœ ˜Kšœœ ˜JšœœŸ˜Jšœœ Ÿ˜Jšœœ Ÿ˜JšœœŸ˜Jšœœ˜-Jšœœ˜Jšœœ˜!Jšœ œ˜Jšœ œ˜)J˜—™ Kšœ œ˜ Kšœ œœ ˜7Kš œ œœœœ ˜.—šฯb™KšœŸ<˜UKšœ%Ÿ˜>Kšœ œŸ$˜>Kšœœœ˜Kšœ œœ˜Kšœœœ˜KšœŸ#˜8Kšœœ Ÿ˜'—šัbls™šœ™Kšœ œ Ÿ ˜,šœœ™ šœ,™,Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ œ˜Kšœ œ˜—Kšœœ˜Kšœœ˜——šœ ™ Kšœ œ Ÿ ˜+Kšœœ˜Kšœ œ˜Kšœœ˜ Kšœœ˜ Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ œ˜K˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜—šœ&™&Jšœ œ Ÿ ˜*Jšœœ ˜Jšœ œ˜—šœ™Kšœ œ˜Kšœ œ˜Kšœœ˜Kšœ œ˜Kšœ œ˜—šœ™Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜—šœ™Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœœ˜Kšœ œ˜—šœ™Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœ œ˜Kšœœ˜——šข ™ šžœœœ˜'Kšœ:™:Kšœ ˜ Kšœ˜Kšœ œ2œœ˜LKšœœ ˜$šœ œœ˜/Kšœ ˜ šœœ˜'Kšœ(˜(šœœœ˜šœœœ ˜-Kšœ˜Kšœ#˜%Kšœ#˜%Kšœ#˜%Kšœ#˜%Kšœœ˜—Kšœ*˜*Kšœ$˜$K˜—Kšœ˜—šœœœ˜,Kšœ‹˜‹Kšœ˜—šœœ˜0Kšœ'˜'šœœ˜'šœ˜šœ ˜Kšœœ/˜BKšœœ2˜EK˜—šœ ˜Kšœœ2˜EKšœœ5˜HK˜—Kšœœ˜—Kšœ˜—Kšœ0˜0Kšœ˜—Kšœ*˜*Kšœ,˜,Kšœ˜—Jšœ œœœ˜K˜K˜—šž œœ˜Kšœœ ˜.Jšœ˜J˜Jšœ˜Jšœ˜Kšœ˜K˜K˜—šžœœ˜Kšœœ˜-Jšœ œœœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ œœœ˜Jšœ œœœ˜J˜J˜—šž œœ˜Kšœœณ™ธK™Kšœ˜K˜ Kšœ˜K˜ Kšœ˜šœœ˜'Kšœœ˜Kšœ˜Kšœœ>˜GKšœœ"˜8Kšœ˜—Kšœ7œœ˜\K˜K˜—šžœœ%˜2Kšœ˜Kšœ˜Kšœ œ˜K˜Kšœ.œ œ ˜IKšœ+˜+Kšœ/Ÿ˜HKšœœ˜'K˜š˜šœ-˜4Kšœœœ˜*Kšœ4˜4Kšœœ˜'Kšœ˜—K˜šœ,œ˜4Kšœœœ˜*Kšœ4˜4Kšœœ˜'Kšœ˜K˜—Kšœ˜Kšœ˜—K˜KšœŸ˜)Kšœ˜K˜K˜—šžœœ3˜?Kšœ˜Kšœ˜Kšœœ˜K˜Kšœ8œ œ œ ˜bKšœ+˜+Kšœ1˜1Kšœœ˜&K˜š˜šœ-˜4Kšœœœ˜*Kšœ4˜4Kšœœ˜&Kšœ˜—K˜šœ6˜=Kšœœœ˜*Kšœ:˜:Kšœœ˜&Kšœ˜—K˜šœ,œ˜4Kšœœœ˜*Kšœ4˜4Kšœœ˜&Kšœ˜K˜—K˜šœ5œ˜=Kšœœœ˜*Kšœ:˜:Kšœœ˜&Kšœ˜K˜—Kšœ˜Kšœ˜—K˜KšœŸ˜)Kšœ˜K˜K˜—šžœœ3˜?Kšœ˜Kšœ˜Kšœœ˜K˜Kšœ8œ œ œ ˜bKšœ+˜+Kšœ1˜1Kšœœ˜&š˜šœ/˜6Kšœœœ˜*Kšœ4˜4Kšœœ˜&Kšœ˜—K˜šœ4˜;Kšœœœ˜*Kšœ:˜:Kšœœ˜&Kšœ˜—K˜šœ.œ˜6Kšœœœ˜*Kšœ4˜4Kšœœ˜&Kšœ˜K˜—K˜šœ3œ˜;Kšœœœ˜*Kšœ:˜:Kšœœ˜&Kšœ˜K˜—Kšœ˜Kšœ˜—K˜KšœŸ˜)Kšœ˜K˜K˜—šžœœ3˜?Kšœ˜K˜K˜K˜—šžœœ3˜?Kšœ˜K˜K˜K˜—šž œœd˜sK˜Kšœ#˜#Kšœ#˜#K˜K˜—šžœœFœ˜rKšœ˜Kšœ ˜ šœœ˜Kšœ$˜$Kšœ˜—šœ˜Kšœ$˜$Kšœ˜—K˜K˜—šžœœ?œ˜iKšœ˜šœ˜Kšœ%˜%Kšœ˜—K˜K˜—šž œœ(œ$˜dšžœœœ,˜MKšœœ˜?Kšœ ˜ šœ"œ˜)šœœ˜Kšœ ˜ Kšœ˜Kšœœ˜Kšœ˜Kšœ˜Kšœœœ˜K˜——Kšœ˜Kšœ˜K˜K˜—Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœœ˜Kšœ œœ˜@Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ(˜(Kšœœ6œ œ ˜\K˜K˜—š žœœPœ œœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜šœœœ˜Kšœ!˜!K˜Kšœœœœ˜$Kšœ˜—šœœ%œ˜7Kšœ˜Kšœœ˜ K˜—šœœœ˜K–V[es: TOCTest.EdgeStep, e: TOCTest.Edge, p: TOCTest.Phase, ch: TOCTest.Channel]šœ˜K˜Kšœœœœ ˜,š˜šœ=˜=Kšœœ œ œ ˜5Kšœœœœ˜*Kšœ œ ˜—Kšœ(˜0—Kšœ˜—K˜K˜—šž œ œ@˜TKš œ œœ œœ œœ˜lJ˜J˜—šž œœ˜šœœ˜&Kšœ˜Kšœ˜—K˜K˜—šž œœ˜'šœ œ˜Kšœ˜Kšœ˜—Kšœ˜Kšœ˜K˜K˜—šžœœDœ$˜}šœœ˜Kšœ œ ˜Kšœ œ ˜Kšœœ œ ˜(Kšœœ œ ˜(Kšœœ ˜—K˜K˜—šž œœง˜บKšœœ˜2Kšœœ˜1Kšœœ œœ˜@Kšœœ œœ˜?Kšœœ œœ˜?Kšœ=˜=Kšœ>˜>K˜K˜—šžœœœ œ˜DKšœ˜Kšœ œœ˜+Kš œ œœœ œœœ ˜FKšœœ˜ Kšœ œœŸ˜JK˜K˜—š žœœ(œœœ˜\Kšœ˜Kšœ œœ˜(Kšœœ œœ ˜0Kšœœ œœ ˜0Kšœœ˜ Kšœ œ˜Kšœ œœŸ˜KK˜K˜—šžœœœœ˜AKšœœœ ˜CK˜K˜—šžœœ7˜Eš œ œœ4œœ˜hšœ˜K˜K˜K˜K˜Kšœœ˜—Kš œ œ œ œ œ ˜D—Kšœ˜Kšœ˜Kšœ(˜(šœœ˜+Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœœ˜—Kšœ(˜(Kšœ˜K˜K˜—šžœœœ˜MKšœ˜Kšœ(˜(šœœ˜+Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœœ˜—Jšœ ˜ Kšœ˜š œ œœ3œœ˜gšœ˜K˜K˜K˜K˜Kšœœ˜—Kš œ œ œ œ œ ˜D—K˜K˜—šž œœ˜,Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜K˜—šž œœœ˜5Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜K˜—šžœœœ˜!šœœ˜Kšœœœœ˜+Kšœ˜Kšœ ˜$Kšœ!˜%KšŸ9™9Kšœ:˜:K˜—K˜K˜—šžœœ˜+Kšœ˜Kšœ˜Jšœ˜Jšœœœœ˜3Jšœ˜Jšœœœœ˜3J˜J˜—šž œœ˜*Kšœ˜Kšœ˜Jšœ˜Jšœœœœ˜3Jšœ˜Jšœœœœ˜3J˜J˜—šžœœ˜-Kšœœ*˜8Kšœ˜Kšœ˜Jšœœœœ˜4Jšœ˜Jšœœœœ˜3Kšœ˜K˜—šžœœ˜-Kšœœ*˜8Kšœ˜Kšœ˜Jšœ˜Jšœœœœ˜3Jšœœœœ˜4Kšœ˜K˜—šžœœ˜*J˜Jšœœ&œœœ5œœ˜’Kšฯf+ะcf˜Fšœ˜˜Kšฃ+ค˜JKšฃ+ค˜JKšฃ+ค˜JKšฃ+ค˜JKšœ˜—˜Kšฃ+ค˜JKšฃ+ค˜JKšฃ+ค˜JKšฃ+ค˜JKšœ˜—˜Kšฃ+ค˜JKšฃ+ค˜JKšฃ+ค˜JKšฃ+ค˜JK˜—˜Kšฃ+ค˜JKšฃ+ค˜JKšฃ+ค˜JKšฃ+ค˜JK˜—Kšœœ˜—Kšœ˜Kšœ˜Kšœ œœœ˜K˜K˜—šžœœ-œ˜[Kšœ˜Kšœ˜šœœ˜Kšœ ˜ Kšœ˜—K˜K˜—šž œ œ#œœ˜KJšœœ˜šœœœ˜šœœ"œ˜ZKšœ˜!—Kšœ˜—Jšœœœœ˜SJšœ˜J˜J˜—šž œœ"˜1Jšœœœœ œœœœœœ˜gKšœœ%œœœœ œ œ œ œ œœ˜˜J˜J˜—šžœœœ˜5Jšœœ˜