<<[Indigo]®>Rosemary.DF=>RoseTestingImpl.Mesa>> <> <> <> DIRECTORY AMBridge, AMTypes, Ascii, BitTwiddling, Cucumber, IO, OrderedSymbolTableRef, PrincOps, PrincOpsUtils, Rope, RoseClocks, RoseCreate, RoseRun, RoseStateIO, RoseTypes; RoseTestingImpl: CEDAR PROGRAM IMPORTS AMBridge, AMTypes, Ascii, BitTwiddling, Cucumber, IO, OrderedSymbolTableRef, PrincOpsUtils, Rope, RoseClocks, RoseCreate, RoseRun, RoseStateIO, RoseTypes EXPORTS RoseCreate, RoseRun, RoseTypes = BEGIN OPEN RoseTypes, RoseRun, RoseCreate; InterfaceMismatch: PUBLIC ERROR [cell: Cell, index: CARDINAL, expected, got: NodeType] = CODE; TV: TYPE = AMTypes.TV; Type: TYPE = AMTypes.Type; WrapTop: PUBLIC PROC [rootName, typeName: ROPE, decider: ExpandDeciderClosure, clocks: ROPE _ NIL, sim: Simulation] = BEGIN rootType: CellType; rootType _ GetWrappingType[typeName, clocks]; CreateTopCell[instanceName: rootName, typeName: rootType.name, decider: decider, sim: sim]; END; GetWrappingType: PROC [subName, clocks: ROPE] RETURNS [wrappingType: CellType] = BEGIN wrappingName: ROPE = subName.Cat["-wrap-", clocks]; subType: CellType; IF (wrappingType _ GetCellType[wrappingName]) # NIL THEN RETURN; IF (subType _ GetCellType[subName]) = NIL THEN ERROR Error[msg: "No such type", data: subName]; wrappingType _ RegisterCellType[ name: wrappingName, expandProc: ExpandWrapper, evals: [], ports: NEW [PortsRep[0]], typeData: NEW [WrapTypeRep _ [subType, clocks]]]; END; WrapType: TYPE = REF WrapTypeRep; WrapTypeRep: TYPE = RECORD [ subType: CellType, clocks: ROPE]; ExpandWrapper: PROC [thisCell: Cell, to: ExpansionReceiver] --ExpandProc-- = BEGIN wc: WrapType _ NARROW[thisCell.type.typeData]; IF wc.clocks # NIL THEN { clockType: CellType _ RoseClocks.ClockGen[[]]; PerClock: PROC [key: Key, nodeName: ROPE] = { portIndex: PortIndex _ nilPortIndex; WITH key SELECT FROM x: Key.position => portIndex _ x.index; x: Key.keyword => portIndex _ GetIndex[clockType.ports, x.name]; ENDCASE => ERROR; [] _ to.class.NodeInstance[erInstance: to.instance, name: nodeName, type: clockType.ports[portIndex].type]; }; EnumerateConnections[wc.clocks, PerClock]; [] _ to.class.CellInstance[erInstance: to.instance, instanceName: "clockGen", typeName: clockType.name, interfaceNodes: wc.clocks]; }; EnsureEnvironment[in: thisCell, forType: wc.subType, to: to]; [] _ to.class.CellInstance[erInstance: to.instance, instanceName: LowerFirst[wc.subType.name], typeName: wc.subType.name, interfaceNodes: ""]; END; LowerFirst: PROC [u: ROPE] RETURNS [l: ROPE] = { IF u.Length[] = 0 THEN RETURN [u]; l _ Rope.FromChar[Ascii.Lower[u.Fetch[0]]].Concat[u.Substr[start: 1]]}; EnsureEnvironment: PUBLIC PROC [in: Cell, forType: CellType, to: ExpansionReceiver] = BEGIN FOR portIndex: CARDINAL IN [0 .. forType.ports.length) DO port: Port = forType.ports[portIndex]; node: Node _ NARROW[in.internalNodes.Lookup[port.name]]; s: Strength _ charge; iv: ROPE _ NIL; IF node # NIL THEN LOOP; IF port.name.Equal["gnd", FALSE] THEN {s _ input; iv _ "gnd"} ELSE IF port.name.Equal["vdd", FALSE] THEN {s _ input; iv _ "vdd"} ELSE IF port.input AND NOT port.output THEN s _ input ELSE IF port.output AND NOT port.input THEN s _ chargeWeak; [] _ to.class.NodeInstance[erInstance: to.instance, name: port.name, type: port.type, initialValue: iv, initialValueFormat: IF iv # NIL THEN "init" ELSE NIL, initData: NEW [Strength _ s]]; ENDLOOP; END; ProcToInt: PROC [p: PROC ANY RETURNS ANY] RETURNS [i: INT] = TRUSTED {i _ LOOPHOLE[p, INTEGER]}; MakeTesterPorts: PROC [testee: Ports, alwaysOutput, alwaysInput: BOOL _ FALSE] RETURNS [tester: Ports] = BEGIN tester _ NEW [PortsRep[testee.length]]; FOR i: CARDINAL IN [0 .. testee.length) DO tester[i] _ testee[i]; IF testee[i].instructionsSimple # testee[i].type.simple THEN { IF testee[i].type.simple THEN ERROR; tester[i].type _ testee[i].type.procs.SimpleEquivalent[testee[i].type]; }; tester[i].input _ alwaysInput OR testee[i].output; tester[i].output _ alwaysOutput OR testee[i].input; ENDLOOP; END; CreateTest: PUBLIC PROC [rootName, typeName, testName: ROPE, decider: ExpandDeciderClosure, sim: Simulation, dual: BOOL _ FALSE] = BEGIN testeeType: CellType _ GetCellType[typeName]; test: CellTest; testerName: ROPE = "tester"; testeeName: ROPE = "testee"; singleType, testerType: CellType; singleTypeName, topTypeName: ROPE; sd: SingleData; td: TesterData; cth: CellTestHandle; IF testeeType = NIL THEN ERROR Error[msg: "No such type", data: typeName]; test _ GetTest[name: testName, from: testeeType]; sd _ NEW [SingleDataRep _ [ testeeType: testeeType, testerTypeName: IO.PutFR["%g-Tester(%g)", IO.rope[typeName], IO.rope[testName]], testeeTypeName: typeName, testerName: testerName, testeeName: testeeName]]; td _ NEW [TesterDataRep _ [ testeeType: testeeType, test: test]]; singleTypeName _ IO.PutFR["%g-TesterSingle(%g)", IO.rope[typeName], IO.rope[testName]]; IF (testerType _ GetCellType[sd.testerTypeName]) = NIL THEN testerType _ RegisterCellType[ name: sd.testerTypeName, ioCreator: CreateTesterIO, initializer: InitTester, evals: testerEvals, ports: MakeTesterPorts[testee: testeeType.ports, alwaysOutput: TRUE], driveCreator: CreateTesterDrive, typeData: td]; IF (singleType _ GetCellType[singleTypeName]) = NIL THEN singleType _ RegisterCellType[ name: singleTypeName, expandProc: ExpandTestSingle, evals: [], ports: NEW [PortsRep[0]], typeData: sd]; IF dual THEN { dualTypeName: ROPE _ Rope.Cat[singleTypeName, "-Dual"]; dualType: CellType _ GetCellType[dualTypeName]; dd: DualData _ NEW [DualDataRep _ [singleTypeName]]; IF dualType = NIL THEN dualType _ RegisterCellType[ name: dualTypeName, expandProc: ExpandDualTester, evals: [], ports: NEW [PortsRep[0]], typeData: dd]; topTypeName _ dualTypeName; cth _ NEW [CellTestHandleRep.dual _ [sim: sim, test: test, testerType: testerType, testeeType: testeeType, variant: dual[]]]; } ELSE { topTypeName _ singleTypeName; cth _ NEW [CellTestHandleRep.single _ [sim: sim, test: test, testerType: testerType, testeeType: testeeType, variant: single[]]]; }; sim.cth _ cth; CreateTopCell[instanceName: rootName, typeName: topTypeName, decider: decider, sim: sim]; WITH cth SELECT FROM scth: SingleCTH => { scth.testee _ LookupCell[path: LIST[testeeName], from: sim.root]; scth.tester _ LookupCell[path: LIST[testerName], from: sim.root]; }; dcth: DualCTH => { dcth.testee[1] _ LookupCell[path: LIST["1", testeeName], from: sim.root]; dcth.tester[1] _ LookupCell[path: LIST["1", testerName], from: sim.root]; dcth.testee[2] _ LookupCell[path: LIST["2", testeeName], from: sim.root]; dcth.tester[2] _ LookupCell[path: LIST["2", testerName], from: sim.root]; }; ENDCASE => ERROR; END; TesterData: TYPE = REF TesterDataRep; TesterDataRep: TYPE = RECORD [ testeeType: CellType, test: CellTest]; SingleData: TYPE = REF SingleDataRep; SingleDataRep: TYPE = RECORD [ testeeType: CellType, testerTypeName, testeeTypeName, testerName, testeeName: ROPE]; DualData: TYPE = REF DualDataRep; DualDataRep: TYPE = RECORD [ singleTypeName: ROPE]; ExpandTestSingle: PROC [thisCell: Cell, to: ExpansionReceiver] --ExpandProc-- = BEGIN sd: SingleData _ NARROW[thisCell.type.typeData]; FOR i: INT IN [0 .. sd.testeeType.ports.length) DO p: Port _ sd.testeeType.ports[i]; [] _ to.class.NodeInstance[erInstance: to.instance, name: p.name, type: p.type]; ENDLOOP; [] _ to.class.CellInstance[erInstance: to.instance, instanceName: sd.testeeName, typeName: sd.testeeTypeName, interfaceNodes: ""]; [] _ to.class.CellInstance[erInstance: to.instance, instanceName: sd.testerName, typeName: sd.testerTypeName, interfaceNodes: ""]; END; ExpandDualTester: PROC [thisCell: Cell, to: ExpansionReceiver] --ExpandProc-- = BEGIN dd: DualData _ NARROW[thisCell.type.typeData]; [] _ to.class.CellInstance[erInstance: to.instance, instanceName: "1", typeName: dd.singleTypeName, interfaceNodes: ""]; [] _ to.class.CellInstance[erInstance: to.instance, instanceName: "2", typeName: dd.singleTypeName, interfaceNodes: ""]; END; testerEvals: EvalProcs _ [ PropQ: PropTesterQUD, PropUD: PropTesterQUD, ValsChanged: TesterValsChanged, EvalSimple: TesterSimple]; CellTestHandle: TYPE = REF CellTestHandleRep; CellTestHandleRep: PUBLIC TYPE = RECORD [ sim: Simulation, parms: RoseRun.TestParms _ NIL, switchInstructionsAsAny: REF ANY _ NIL, switchInstructionsAsWP: WordPtr _ NIL, simpleInstructionsAsAny: REF ANY _ NIL, simpleInstructionsAsWP: WordPtr _ NIL, driveAsAny: REF ANY _ NIL, driveAsDrive: Drive _ NIL, test: CellTest, testerType, testeeType: CellType, variant: SELECT kind: * FROM single => [tester, testee: Cell _ NIL], dual => [ tester, testee: ARRAY [1 .. 2] OF Cell _ ALL[NIL], altSwitchInstructionsAsAny: REF ANY _ NIL, altSwitchInstructionsAsWP: WordPtr _ NIL, altSimpleInstructionsAsAny: REF ANY _ NIL, altSimpleInstructionsAsWP: WordPtr _ NIL ], ENDCASE ]; SingleCTH: TYPE = REF CellTestHandleRep.single; DualCTH: TYPE = REF CellTestHandleRep.dual; Waiting: TYPE = {Tester, Testee}; NarrowToCellTestHandle: PUBLIC PROC [any: REF ANY] RETURNS [cth: CellTestHandle] = {cth _ NARROW[any]}; GetSimulationFromCellTestHandle: PUBLIC PROC [cth: CellTestHandle] RETURNS [sim: Simulation] = {sim _ cth.sim}; TransferCellTestHandle: PROC [whole: REF ANY, part: Cucumber.Path, where: IO.STREAM, direction: Cucumber.Direction, data: REF ANY] --Cucumber.PartTransferProc-- = TRUSTED BEGIN cth: CellTestHandle _ NARROW[whole]; SELECT part.first FROM $sim, $tester, $testee, $testeeInitData, $switchInstructionsAsAny, $simpleInstructionsAsAny, $driveAsDrive => NULL; $parms => Cucumber.Transfer[what: cth.parms, where: where, direction: direction]; $switchInstructionsAsWP => RoseStateIO.TransferWords[cth.switchInstructionsAsWP, cth.testerType.switchWordCount, where, direction]; $simpleInstructionsAsWP => RoseStateIO.TransferWords[cth.simpleInstructionsAsWP, cth.testerType.simpleWordCount, where, direction]; $driveAsAny => Cucumber.Transfer[what: cth.driveAsAny, where: where, direction: direction]; ENDCASE => ERROR; END; CreateTesterIO: PROC [ct: CellType, switch: BOOL] RETURNS [ioAsAny: REF ANY] --IOCreator-- = BEGIN td: TesterData _ NARROW[ct.typeData]; ioAsAny _ td.testeeType.ioCreator[td.testeeType, switch]; END; CreateTesterDrive: PROC [ct: CellType] RETURNS [ioAsAny: REF ANY] --DriveCreator-- = BEGIN td: TesterData _ NARROW[ct.typeData]; ioAsAny _ td.testeeType.driveCreator[td.testeeType]; END; InitTester: PROC [cell: Cell] --Initializer-- = BEGIN td: TesterData _ NARROW[cell.type.typeData]; cth: CellTestHandle _ cell.sim.cth; cth.switchInstructionsAsAny _ td.testeeType.ioCreator[td.testeeType, TRUE]; cth.switchInstructionsAsWP _ LOOPHOLE[cth.switchInstructionsAsAny]; cth.simpleInstructionsAsAny _ td.testeeType.ioCreator[td.testeeType, FALSE]; cth.simpleInstructionsAsWP _ LOOPHOLE[cth.simpleInstructionsAsAny]; cth.driveAsAny _ NIL; cth.driveAsDrive _ NIL; IF cell.type.driveCreator # NIL THEN { driveAsAny: REF ANY _ td.testeeType.driveCreator[td.testeeType]; IF driveAsAny # NIL THEN { driveAsTV: TV; gotSize: INT; neededSize: INT _ SIZE[DriveRep[cell.type.ports.length]]; TRUSTED {driveAsTV _ AMBridge.TVForReferent[driveAsAny]}; gotSize _ AMTypes.TVSize[driveAsTV]; IF gotSize < neededSize THEN ERROR Error[IO.PutFR["DriveCreator for cell type %g returned one too small", IO.rope[cell.type.name]], driveAsAny]; cth.driveAsAny _ driveAsAny; TRUSTED {cth.driveAsDrive _ LOOPHOLE[driveAsAny]}; FOR pi: NAT IN [0 .. cell.type.ports.length) DO cth.driveAsDrive[pi] _ IF cell.type.ports[pi].input THEN test ELSE IF cell.type.ports[pi].name.Equal["vdd", FALSE] OR cell.type.ports[pi].name.Equal["gnd", FALSE] THEN input ELSE drive; ENDLOOP; }; }; cell.realCellStuff.state _ cth; END; PropTesterQUD: CellProc--PROC [cell: Cell]-- = BEGIN cth: CellTestHandle _ NARROW[cell.realCellStuff.state]; CopyIO[from: cth.switchInstructionsAsWP, to: cell.realCellStuff.switchIOAsWP, by: cell.type.ports, what: forward, simple: FALSE, drive: NIL]; END; DirectionInstruction: TYPE = {none, forward, backward, check, moveByDrive, checkTests, checkIns}; CopyIO: PROC [from, to: WordPtr, by: Ports, what: DirectionInstruction, simple: BOOLEAN, drive: Drive, cell: Cell _ NIL] = BEGIN OPEN BitTwiddling; bbTableSpace: PrincOps.BBTableSpace; bbTable: PrincOps.BitBltTablePtr; TRUSTED {bbTable _ PrincOpsUtils.AlignedBBTable[@bbTableSpace]}; FOR portIndex: CARDINAL IN [0 .. by.length) DO port: Port _ by[portIndex]; di: DirectionInstruction; bitCount, leftPad: INTEGER; IF port.type.simple # simple THEN LOOP; [data: bitCount, leftPad: leftPad] _ port.type.procs.Bits[port.type]; { field: Field _ IF port.type.simple THEN port.simple ELSE port.switch; fromPtr: Ptr _ OffsetPtr[WPField[from, field], leftPad]; toPtr: Ptr _ OffsetPtr[WPField[to, field], leftPad]; SELECT what FROM moveByDrive => IF port.type.simple THEN { di _ SELECT drive.drives[portIndex] FROM test => none, see => backward, ignore => none, chargeWeak, chargeMediumWeak, charge, chargeMediumStrong, chargeStrong, chargeVeryStrong, driveWeak, driveMediumWeak, drive, driveMediumStrong, driveStrong, driveVeryStrong, input => forward, ENDCASE => ERROR; } ELSE ERROR; checkTests => IF port.type.simple THEN { di _ SELECT drive.drives[portIndex] FROM test => check, see => none, ignore => none, chargeWeak, chargeMediumWeak, charge, chargeMediumStrong, chargeStrong, chargeVeryStrong, driveWeak, driveMediumWeak, drive, driveMediumStrong, driveStrong, driveVeryStrong, input => none, ENDCASE => ERROR; } ELSE ERROR; checkIns => IF port.type.simple THEN { di _ SELECT drive.drives[portIndex] FROM test => check, see => check, ignore => none, chargeWeak, chargeMediumWeak, charge, chargeMediumStrong, chargeStrong, chargeVeryStrong, driveWeak, driveMediumWeak, drive, driveMediumStrong, driveStrong, driveVeryStrong, input => none, ENDCASE => ERROR; } ELSE ERROR; none, forward, backward, check => di _ what; ENDCASE => ERROR; SELECT di FROM none => NULL; forward => Copy[fromPtr, toPtr, bitCount, bbTable]; backward => Copy[toPtr, fromPtr, bitCount, bbTable]; check => IF NOT Equal[fromPtr, toPtr, bitCount] THEN SIGNAL Warning[IO.PutFR["test failed on %g", IO.rope[RoseCreate.LongPortName[cell, portIndex]]]]; moveByDrive, checkTests => ERROR; ENDCASE => ERROR; } ENDLOOP; END; Fmt: PROC [n: Node, p: Ptr] RETURNS [rope: ROPE] = { fmt: Format _ n.type.procs.GetFormat[n.type, ""]; rope _ fmt.FormatValue[n, fmt, p]; }; TesterValsChanged: PROC [cell: Cell, perturb: PROC [portIndex: PortIndex]] --SimpleEval-- = BEGIN OPEN BitTwiddling; cth: CellTestHandle _ NARROW[cell.realCellStuff.state]; swp: WordPtr _ WITH cth SELECT FROM dcth: DualCTH => SELECT cell FROM dcth.tester[1] => dcth.switchInstructionsAsWP, dcth.tester[2] => dcth.altSwitchInstructionsAsWP, ENDCASE => ERROR, scth: SingleCTH => scth.switchInstructionsAsWP, ENDCASE => ERROR; FOR portIndex: NAT IN [0 .. cell.type.ports.length) DO port: Port _ cell.type.ports[portIndex]; IF (NOT port.type.simple) THEN { leftPad, bits: INT; [data: bits, leftPad: leftPad] _ port.type.procs.Bits[port.type]; port.type.procs.CopyVal[ nt: port.type, from: OffsetPtr[WPField[cell.realCellStuff.switchIOAsWP, port.switch], leftPad], to: OffsetPtr[WPField[swp, port.switch], leftPad] ]; }; ENDLOOP; END; TesterSimple: PROC [cell: Cell, perturb: PROC [portIndex: PortIndex]] --SimpleEval-- = BEGIN cth: CellTestHandle _ NARROW[cell.realCellStuff.state]; swp: WordPtr _ cth.simpleInstructionsAsWP; cellDrive: Drive _ cell.realCellStuff.newDrive; WITH cth SELECT FROM dcth: DualCTH => SELECT cell FROM dcth.tester[1] => NULL; dcth.tester[2] => { swp _ dcth.altSimpleInstructionsAsWP; }; ENDCASE => ERROR; scth: SingleCTH => NULL; ENDCASE => ERROR; FOR pi: PortIndex IN [0 .. cell.type.ports.length) DO cellDrive[pi] _ MAX[cth.driveAsDrive[pi], FIRST[Strength]]; ENDLOOP; CopyIO[from: swp, to: cell.realCellStuff.newIOAsWP, by: cell.type.ports, what: moveByDrive, simple: TRUE, drive: cth.driveAsDrive]; cell _ cell; END; Eval: PUBLIC PROC [handle: CellTestHandle, returnAfter: ReturnAfter _ returnWhenSettled] RETURNS [happened: StepType] = BEGIN iowp: WordPtr _ NIL; tester: Cell _ NIL; WITH handle SELECT FROM dcth: DualCTH => { ScheduleCell[dcth.tester[1]]; ScheduleCell[dcth.tester[2]]; PerturbDifferences[dcth, dcth.tester[1]]; PerturbDifferences[dcth, dcth.tester[2]]; CopyIO[from: dcth.simpleInstructionsAsWP, to: dcth.altSimpleInstructionsAsWP, by: dcth.testerType.ports, what: forward, simple: TRUE, drive: dcth.driveAsDrive]; CopyIO[from: dcth.switchInstructionsAsWP, to: dcth.altSwitchInstructionsAsWP, by: dcth.testerType.ports, what: forward, simple: FALSE, drive: dcth.driveAsDrive]; }; scth: SingleCTH => { ScheduleCell[scth.tester]; PerturbDifferences[scth, scth.tester]; }; ENDCASE => ERROR; IF handle.parms.stopBefore THEN SIGNAL Stop["About to eval"]; DO happened _ StepSim[handle.sim]; IF (IF happened IN MaskableStepType THEN returnAfter[happened] ELSE TRUE) THEN EXIT; ENDLOOP; WITH handle SELECT FROM dcth: DualCTH => { tester _ dcth.tester[1]; CopyIO[from: dcth.simpleInstructionsAsWP, to: dcth.altSimpleInstructionsAsWP, by: dcth.testerType.ports, what: checkIns, simple: TRUE, drive: dcth.driveAsDrive, cell: tester]; CopyIO[from: dcth.switchInstructionsAsWP, to: dcth.altSwitchInstructionsAsWP, by: dcth.testerType.ports, what: check, simple: FALSE, drive: dcth.driveAsDrive, cell: tester]; }; scth: SingleCTH => { tester _ scth.tester; }; ENDCASE => ERROR; iowp _ tester.realCellStuff.newIOAsWP; CopyIO[from: handle.simpleInstructionsAsWP, to: iowp, by: handle.testerType.ports, what: checkTests, simple: TRUE, drive: handle.driveAsDrive, cell: tester]; IF handle.parms.stopAfter THEN SIGNAL Stop["Just eval'd"]; END; PerturbDifferences: PROC [cth: CellTestHandle, cell: Cell] = BEGIN OPEN BitTwiddling; FOR portIndex: PortIndex IN [0 .. cell.type.ports.length) DO port: Port _ cell.type.ports[portIndex]; different: BOOL _ FALSE; leftPad: INT; IF port.type.simple THEN LOOP; leftPad _ port.type.procs.Bits[port.type].leftPad; TRUSTED {different _ port.type.procs.CompareUD[ port.type, OffsetPtr[WPField[cth.switchInstructionsAsWP, port.switch], leftPad], OffsetPtr[WPField[cell.realCellStuff.switchIOAsWP, port.switch], leftPad] ]}; IF different THEN PerturbNode[node: cell.interfaceNodes[portIndex], agitator: nilSlot, evenIfInput: TRUE]; ENDLOOP; END; Test: PUBLIC PROC [sim: Simulation, parms: TestParms, testData: REF ANY _ NIL] = BEGIN cth: CellTestHandle _ sim.cth; test: CellTest _ cth.test; state: REF ANY _ NIL; IF test = NIL THEN ERROR Error["Type has no such test proc", cth]; cth.parms _ parms; IF test.stateToo THEN state _ WITH cth SELECT FROM scth: SingleCTH => scth.testee.realCellStuff.state, dcth: DualCTH => IF dcth.testee[1].realCellStuff # NIL THEN dcth.testee[1].realCellStuff.state ELSE IF dcth.testee[2].realCellStuff # NIL THEN dcth.testee[2].realCellStuff.state ELSE ERROR, ENDCASE => ERROR; test.proc[handle: cth, testeeType: cth.testeeType, testData: testData, switchInstructions: cth.switchInstructionsAsAny, simpleInstructions: cth.simpleInstructionsAsAny, driveAsAny: cth.driveAsAny, stateAsAny: state]; WHILE StepSim[sim] # noStep DO NULL ENDLOOP; END; cthHandler: Cucumber.Handler _ NEW [Cucumber.HandlerRep _ [TransferCellTestHandle]]; cthHandler.Register[CODE[CellTestHandleRep]]; END.