DIRECTORY CD, CDBasics, CDCells, CDInstances, CDRects, CDSimpleRules, CDSymbolicObjects, PW, PWPins, PWRoute, Rope, Route, RouteUtil, RTBasic, SymTab; PWRouteImpl: CEDAR PROGRAM IMPORTS CD, CDBasics, CDCells, CDInstances, CDRects, CDSimpleRules, CDSymbolicObjects, PW, PWPins, Rope, Route, RouteUtil, RTBasic, SymTab EXPORTS PWRoute = BEGIN OPEN PWRoute; AbutRouteList: PROC [listOb, bottomOrLeftListOb, topOrRightListOb: PWRoute.Objects, params: RouterParams, isX: BOOL, routeType: RouteType] RETURNS [obj: CD.Object] = BEGIN newListObj: PWRoute.Objects _ NIL; obj1, obj2, topOrRightObj, bottomOrLeftObj, channel: CD.Object; IF listOb = NIL THEN RETURN[NIL]; obj1 _ listOb.first; listOb _ listOb.rest; newListObj _ CONS[obj1, NIL]; FOR l: PWRoute.Objects _ listOb, l.rest WHILE l # NIL DO obj2 _ l.first; IF topOrRightListOb = NIL THEN topOrRightObj _ NIL ELSE {topOrRightObj _ topOrRightListOb.first; topOrRightListOb _ topOrRightListOb.rest}; IF bottomOrLeftListOb = NIL THEN bottomOrLeftObj _ NIL ELSE {bottomOrLeftObj _ bottomOrLeftListOb.first; bottomOrLeftListOb _ bottomOrLeftListOb.rest}; channel _ MakeChannel[obj1, obj2, bottomOrLeftObj, topOrRightObj, NIL, params, isX, routeType]; newListObj _ CONS[obj2, CONS[channel, newListObj]]; obj1 _ obj2; -- just before looping ENDLOOP; newListObj _ PW.Reverse[newListObj]; IF isX THEN obj _ PW.AbutListX[newListObj] ELSE obj _ PW.AbutListY[newListObj]; END; AbutChRouteListX: PUBLIC PROC [ listOb: PWRoute.Objects, bottomListOb: PWRoute.Objects _ NIL, topListOb: PWRoute.Objects _ NIL, params: RouterParams _ defaultRouterParams] RETURNS [obj: CD.Object] = {obj _ AbutRouteList[listOb, bottomListOb, topListOb, params, TRUE, channel]}; AbutChRouteListY: PUBLIC PROC [ listOb: PWRoute.Objects, leftListOb: PWRoute.Objects _ NIL, rightListOb: PWRoute.Objects _ NIL, params: RouterParams _ defaultRouterParams] RETURNS [obj: CD.Object] = {obj _ AbutRouteList[listOb, leftListOb, rightListOb, params, FALSE, channel]}; AbutSbRoute: PUBLIC PROC [ bottomOb, rightOb, topOb, leftOb: CD.Object _ NIL, trunkDir: HorV _ horizontal, params: RouterParams _ defaultRouterParams] RETURNS [obj: CD.Object] = { IF trunkDir = horizontal THEN obj _ MakeChannel[bottomOb, topOb, leftOb, rightOb, NIL, params, FALSE, switchBox] ELSE obj _ MakeChannel[leftOb, rightOb, bottomOb, topOb, NIL, params, TRUE, switchBox]}; Net: TYPE = REF NetRec; NetRec: TYPE = RECORD[ name: Rope.ROPE _ NIL, -- net name pins: Route.PinList _ NIL]; -- the list of pins in the net defaultRouterParams: PUBLIC RouterParams _ NEW[RouterParamsRec _ ["metal", "poly"]]; ParsePins: PROC[obj: CD.Object, tab: SymTab.Ref, side: PWPins.Side, params: RouterParams] = { EachPin: CDSymbolicObjects.InstEnumerator = { IF PWPins.GetSide[obj, inst].side=side THEN InsertPin[tab, inst, side, params]; }; IF obj # NIL THEN [] _ PWPins.EnumerateEdgePins[obj, EachPin]; }; FromSideToSide: PROC [side: PWPins.Side] RETURNS [routeSide: Route.Side] = { routeSide _ SELECT side FROM left => left, right => right, top => top, bottom => bottom, ENDCASE => ERROR; }; OtherSide: PROC [side: Route.Side] RETURNS [opposite: Route.Side] = { opposite _ SELECT side FROM left => right, right => left, top => bottom, bottom => top, ENDCASE => ERROR; }; InsertPin: PROC [tab: SymTab.Ref, inst: CD.Instance, side: PWPins.Side, params: RouterParams] = { found: BOOL; val: REF; net: Net; pin: Route.Pin _ Route.CreatePin[inst, OtherSide[FromSideToSide[side]]]; pinName: Rope.ROPE _ CDSymbolicObjects.GetName[inst]; tabIndex: Rope.ROPE _ IF params.makeTabKeyProc=NIL THEN pinName ELSE params.makeTabKeyProc[inst, params.context]; [found, val] _ SymTab.Fetch[tab, tabIndex]; net _ IF ~found THEN NEW[NetRec _ [pinName]] ELSE NARROW[val]; net.pins _ CONS[pin, net.pins]; [] _ SymTab.Store[tab, tabIndex, net]; }; ShipNets: PROC [tab: SymTab.Ref, routingArea: Route.RoutingArea, trunkAtom: ATOM, params: RouterParams] = { EnterOneNet: SymTab.EachPairAction = BEGIN netName: Rope.ROPE _ NARROW[key]; net: Net _ NARROW[val]; width: Route.Number _ IF params.wireWidthProc=NIL THEN 0 ELSE params.wireWidthProc[net.name, params.context]; properties: Route.PropList _ RouteUtil.PutNumberProp[NIL, Route.trunkWidthKey, width]; Route.IncludeNet[routingArea, net.name, net.pins, properties]; RETURN[FALSE]; END; [] _ SymTab.Pairs[tab, EnterOneNet]; }; DoRoute: PUBLIC PROC [obj1, obj2, bottomOrLeftObj, topOrRightObj: CD.Object, params: RouterParams, isX: BOOL, routeType: RouteType] RETURNS [result: Route.RoutingResult] = { tab: SymTab.Ref; sideCoords: Route.PositionVec; -- the positions of the sides in a "large" coord system r1, r2, rbl, rtr, routingRect: CD.Rect _ [0, 0, 0, 0]; pBottom, pLeft, pTop, pRight: CD.Position; trunkAtom: ATOM; rules: Route.DesignRules _ IF isX THEN -- abutX Route.CreateDesignRules[params.technologyKey, CDSimpleRules.GetLayer[params.technologyKey, params.branchLayer], CDSimpleRules.GetLayer[params.technologyKey, params.trunkLayer], vertical] ELSE -- abutY Route.CreateDesignRules[params.technologyKey, CDSimpleRules.GetLayer[params.technologyKey, params.trunkLayer], CDSimpleRules.GetLayer[params.technologyKey, params.branchLayer], horizontal]; routingArea: Route.RoutingArea _ Route.CreateRoutingArea["Channel", rules]; IF obj1 # NIL THEN r1 _ CD.InterestRect[obj1]; IF obj2 # NIL THEN r2 _ CD.InterestRect[obj2]; IF bottomOrLeftObj # NIL THEN rbl _ CD.InterestRect[bottomOrLeftObj]; IF topOrRightObj # NIL THEN rtr _ CD.InterestRect[topOrRightObj]; IF isX THEN { -- AbutX pBottom _ [0, - rbl.y2 + rbl.y1]; pLeft _ [- r1.x2 + r1.x1, 0]; pTop _ [0, MAX[r1.y2 - r1.y1, r2.y2 - r2.y1]]; pRight _ [MAX[rbl.x2 - rbl.x1, rtr.x2 - rtr.x1], 0]; Route.IncludeRoutingAreaSide[routingArea, left, [r1.x1, r1.y1]]; Route.IncludeRoutingAreaSide[routingArea, right, [r2.x1, r2.y1]]; Route.IncludeRoutingAreaSide[routingArea, bottom, [rbl.x1, rbl.y1]]; Route.IncludeRoutingAreaSide[routingArea, top, [rtr.x1, rtr.y1]]; trunkAtom _ $verticalWidth} ELSE { -- AbutY pBottom _ [0, - r1.y2 + r1.y1]; pLeft _ [- rbl.x2 + rbl.x1, 0]; pTop _ [0, MAX[rbl.y2 - rbl.y1, rtr.y2 - rtr.y1]]; pRight _ [MAX[r1.x2 - r1.x1, r2.x2 - r2.x1], 0]; Route.IncludeRoutingAreaSide[routingArea, bottom, [r1.x1, r1.y1]]; Route.IncludeRoutingAreaSide[routingArea, top, [r2.x1, r2.y1]]; Route.IncludeRoutingAreaSide[routingArea, left, [rbl.x1, rbl.y1]]; Route.IncludeRoutingAreaSide[routingArea, right, [rtr.x1, rtr.y1]]; trunkAtom _ $horizontalWidth}; tab _ SymTab.Create[mod: 17, case: TRUE]; -- the table of all nets ParsePins[obj1, tab, IF isX THEN right ELSE top, params]; ParsePins[obj2, tab, IF isX THEN left ELSE bottom, params]; ParsePins[topOrRightObj, tab, IF isX THEN bottom ELSE left, params]; ParsePins[bottomOrLeftObj, tab, IF isX THEN top ELSE right, params]; ShipNets[tab, routingArea, trunkAtom, params]; sideCoords _ [pBottom, pTop, pLeft, pRight]; routingRect _ [0, 0, pRight.x, pTop.y]; SELECT routeType FROM channel => result _ Route.ChannelRoute[routingArea, sideCoords, routingRect, params.opt, params.signalSinglePinNets, params.signalCoincidentPins]; switchBox => result _ Route.SwitchBoxRoute[routingArea, sideCoords, routingRect, params.opt, params.signalSinglePinNets, params.signalCoincidentPins, params.okToDiddleLLPins, params.okToDiddleURPins]; ENDCASE; IF result.incompleteNets # NIL AND params.signalIncomplete THEN Route.Signal[noResource, Rope.Cat["Incomplete routing of nets: ", RopeFromList[result.incompleteNets]]]; IF result.breakAtExitNets # NIL AND params.signalBreakAtExit THEN Route.Signal[noResource, Rope.Cat["Nets were divided at channel exit: ", RopeFromList[result.breakAtExitNets]]]}; GetRouting: PUBLIC PROC [result: Route.RoutingResult, retrieveRect: RefRect _ NIL, params: RouterParams _ defaultRouterParams] RETURNS [channel: CD.Object] ~ { channel _ Route.RetrieveRouting[result, result.routingArea.name, retrieveRect, NIL, params.viaTable].object; Route.Destroy[result.routingArea]}; MakeChannel: PUBLIC PROC [obj1, obj2, bottomOrLeftObj, topOrRightObj: CD.Object, retrieveRect: RefRect, params: RouterParams, isX: BOOL, routeType: RouteType] RETURNS [channel: CD.Object] = { result: Route.RoutingResult_ DoRoute[obj1, obj2, bottomOrLeftObj, topOrRightObj, params, isX, routeType]; channel _ GetRouting[result, retrieveRect, params].channel; Route.Destroy[result.routingArea]}; KeepAll: PUBLIC SelectNamesProc = {keepIt _ TRUE}; XferPins: PUBLIC PROC [technologyKey: ATOM, template: CD.Object, objSide: PWPins.Side, minWidth: INT, routingLayerDes: Rope.ROPE, selectNameProc: PWRoute.SelectNamesProc _ KeepAll, xferPinsProc: PWRoute.XferPinsProc] RETURNS [cell: CD.Object _ NIL] = { FindPins: CDSymbolicObjects.InstEnumerator = { IF PWPins.GetSide[template, inst].side=objSide THEN { pinLayer: CD.Layer _ CDSymbolicObjects.GetLayer[inst]; IF pinLayer # routingLayer THEN { pwBug: CD.Instance _ CDInstances.NewInst[ob: inst.ob, trans: [[0,0], pwBugOrien]]; xferObj: CD.Object; CDSymbolicObjects.SetLayer[pwBug, pinLayer]; xferObj _ xferPinsProc[technologyKey, pwBug, routingLayer, 0]; needsXfer _ TRUE; width _ MAX[width, IRSize[xferObj].x]}}}; MapPins: PWRoute.ForEachPinProc = { obj _ NIL; IF PWPins.GetSide[template, inst].side = objSide THEN { pinInst: CD.Instance; pinSize: CD.Position; pinOb, wire: CD.Object; pwBug: CD.Instance _ CDInstances.NewInst[ob: inst.ob, trans: [[0,0], pwBugOrien]]; CDSymbolicObjects.SetLayer[pwBug, CDSymbolicObjects.GetLayer[inst]]; wire _ xferPinsProc[technologyKey, pwBug, routingLayer, width]; pinSize _ CDBasics.OrientedSize[CDBasics.SizeOfRect[pwBug.ob.bbox], pwBug.trans.orient]; pinOb _ CDSymbolicObjects.CreatePin[pinSize]; obj _ CDCells.CreateEmptyCell[]; pinInst _ CDCells.IncludeOb[cell: obj, ob: pinOb, trans: [[width-pinSize.x, 0]]].newInst; [] _ CDCells.IncludeOb[cell: obj, ob: wire]; CDSymbolicObjects.SetName[pinInst, CDSymbolicObjects.GetName[inst]]; CDSymbolicObjects.SetLayer[pinInst, routingLayer]; RTBasic.RepositionCell[obj]; }; }; width: INT _ MAX[minWidth, 0]; needsXfer: BOOLEAN _ width > 0; routingLayer: CD.Layer _ CDSimpleRules.GetLayer[technologyKey, routingLayerDes]; pwBugOrien: CD.Orientation _ IF objSide=top OR objSide=bottom THEN rotate90 ELSE original; -- PW problem: inst has wrong orientation!!! IF template # NIL THEN { [] _ PWPins.EnumerateEdgePins[template, FindPins]; IF needsXfer THEN cell _ PWPins.TransferCell[template, objSide, width, MapPins, selectNameProc]; }; }; IRSize: PROC [obj: CD.Object] RETURNS [size: CD.Position] = {size _ CDBasics.SizeOfRect[CD.InterestRect[obj]]}; defaultXferPins: PUBLIC XferPinsProc = { pinLayer: CD.Layer _ CDSymbolicObjects.GetLayer[inst]; polyLayer: CD.Layer _ CDSimpleRules.GetLayer[technologyKey, "poly"]; metalLayer: CD.Layer _ CDSimpleRules.GetLayer[technologyKey, "metal"]; metal2Layer: CD.Layer _ CDSimpleRules.GetLayer[technologyKey, "metal2"]; m1ToM12: INT _ MAX[CDSimpleRules.MinDist[metalLayer, metalLayer], CDSimpleRules.MinDist[metal2Layer, metal2Layer]]; sizeX, pinSizeY: INT; cdLambda: INT _ CD.LayerTechnology[routingLayer].lambda; pinSize: CD.Position _ CDBasics.OrientedSize[CDBasics.SizeOfRect[inst.ob.bbox], inst.trans.orient]; pinSizeY _ pinSize.y; cell _ CDCells.CreateEmptyCell[]; SELECT TRUE FROM pinLayer = routingLayer => { sizeX _ width; IF width > 0 THEN { [] _ CDCells.IncludeOb[cell: cell, ob: CDRects.CreateRect[[sizeX, pinSizeY], pinLayer]]}}; pinLayer = polyLayer => { pContact: CD.Object _ CDSimpleRules.Contact[polyLayer, metalLayer]; contactSize: CD.Position _ CDBasics.SizeOfRect[pContact.bbox]; wireP, mPContact: CD.Object; sizeX _ m1ToM12 + contactSize.x; wireP _ CDRects.CreateRect[[sizeX, pinSizeY], polyLayer]; [] _ CDCells.IncludeOb[cell: cell, ob: wireP]; mPContact _ RouteUtil.StitchVias[[contactSize.x, pinSizeY], polyLayer, metalLayer, cdLambda, NIL]; [] _ CDCells.IncludeOb[cell: cell, ob: mPContact, trans: [[m1ToM12, (pinSizeY-CDBasics.SizeOfRect[mPContact.bbox].y)/2]]]; IF routingLayer = metal2Layer THEN { mVia: CD.Object _ CDSimpleRules.Contact[metalLayer, metal2Layer]; viaSize: CD.Position _ CDBasics.SizeOfRect[mVia.bbox]; wireM2: CD.Object _ CDRects.CreateRect[[m1ToM12, pinSizeY], metal2Layer]; m2M1Via: CD.Object; [] _ CDCells.IncludeOb[cell: cell, ob: wireM2, trans: [[sizeX, 0]]]; sizeX _ sizeX + m1ToM12; m2M1Via _ RouteUtil.StitchVias[[viaSize.x, pinSizeY], metalLayer, metal2Layer, cdLambda, NIL]; [] _ CDCells.IncludeOb[cell: cell, ob: m2M1Via, trans: [[sizeX, (pinSizeY-CDBasics.SizeOfRect[m2M1Via.bbox].y)/2]]]; sizeX _ sizeX + viaSize.x}; IF width > sizeX THEN { wireRL: CD.Object _ CDRects.CreateRect[[width - sizeX, pinSizeY], routingLayer]; [] _ CDCells.IncludeOb[cell: cell, ob: wireRL, trans: [[sizeX, 0]]]; sizeX _ width}}; pinLayer = metalLayer => { IF routingLayer = metal2Layer THEN { mVia: CD.Object _ CDSimpleRules.Contact[metalLayer, metal2Layer]; viaSize: CD.Position _ CDBasics.SizeOfRect[mVia.bbox]; wireM, m2M1Via: CD.Object; sizeX _ m1ToM12 + viaSize.x; wireM _ CDRects.CreateRect[[sizeX, pinSizeY], metalLayer]; [] _ CDCells.IncludeOb[cell: cell, ob: wireM]; m2M1Via _ RouteUtil.StitchVias[[viaSize.x, pinSizeY], metalLayer, metal2Layer, cdLambda, NIL]; [] _ CDCells.IncludeOb[cell: cell, ob: m2M1Via, trans:[[m1ToM12, (pinSizeY-CDBasics.SizeOfRect[m2M1Via.bbox].y)/2]]]; IF width > sizeX THEN { wireM2: CD.Object _ CDRects.CreateRect[[width - sizeX, pinSizeY], metal2Layer]; [] _ CDCells.IncludeOb[cell: cell, ob: wireM2, trans: [[sizeX, 0]]]; sizeX _ width}} ELSE { pContact: CD.Object _ CDSimpleRules.Contact[polyLayer, metalLayer]; contactSize: CD.Position _ CDBasics.SizeOfRect[pContact.bbox]; wireM, mPContact: CD.Object; sizeX _ m1ToM12 + contactSize.x; wireM _ CDRects.CreateRect[[sizeX, pinSizeY], metalLayer]; [] _ CDCells.IncludeOb[cell: cell, ob: wireM]; mPContact _ RouteUtil.StitchVias[[contactSize.x, pinSizeY], polyLayer, metalLayer, cdLambda, NIL]; [] _ CDCells.IncludeOb[cell: cell, ob: mPContact, trans: [[CDBasics.SizeOfRect[wireM.bbox].x, (pinSizeY-CDBasics.SizeOfRect[mPContact.bbox].y)/2]]]; IF width > sizeX THEN { wireP: CD.Object _ CDRects.CreateRect[[width - sizeX, pinSizeY], polyLayer]; [] _ CDCells.IncludeOb[cell: cell, ob: wireP, trans: [[sizeX, 0]]]; sizeX _ width}}}; pinLayer = metal2Layer => { mVia: CD.Object _ CDSimpleRules.Contact[metalLayer, metal2Layer]; viaSize: CD.Position _ CDBasics.SizeOfRect[mVia.bbox]; wireM2, m2M1Via: CD.Object; sizeX _ m1ToM12 + viaSize.x; wireM2 _ CDRects.CreateRect[[sizeX, pinSizeY], metal2Layer]; [] _ CDCells.IncludeOb[cell: cell, ob: wireM2]; m2M1Via _ RouteUtil.StitchVias[[viaSize.x, pinSizeY], metalLayer, metal2Layer, cdLambda, NIL]; [] _ CDCells.IncludeOb[cell: cell, ob: m2M1Via, trans: [[m1ToM12, (pinSizeY-CDBasics.SizeOfRect[m2M1Via.bbox].y)/2]]]; IF routingLayer = polyLayer THEN { pContact: CD.Object _ CDSimpleRules.Contact[metalLayer, polyLayer]; contactSize: CD.Position _ CDBasics.SizeOfRect[pContact.bbox]; wireM: CD.Object _ CDRects.CreateRect[[m1ToM12 + contactSize.x, pinSizeY], metalLayer]; mPContact: CD.Object _ RouteUtil.StitchVias[[contactSize.x, pinSizeY], polyLayer, metalLayer, cdLambda, NIL]; [] _ CDCells.IncludeOb[cell: cell, ob: wireM, trans: [[sizeX, 0]]]; sizeX _ sizeX + m1ToM12; [] _ CDCells.IncludeOb[cell: cell, ob: mPContact, trans: [[sizeX, (pinSizeY-CDBasics.SizeOfRect[mPContact.bbox].y)/2]]]; sizeX _ sizeX + contactSize.x}; IF width > sizeX THEN { wireRL: CD.Object _ CDRects.CreateRect[[width - sizeX, pinSizeY], routingLayer]; [] _ CDCells.IncludeOb[cell: cell, ob: wireRL, trans: [[sizeX, 0]]]; sizeX _ width}; }; ENDCASE; CDCells.SetInterestRect[design: NIL, cell: cell, r: [0, 0, sizeX, pinSizeY]]}; RopeFromList: PROC [list: LIST OF Rope.ROPE] RETURNS [rope: Rope.ROPE] ~ { -- make a rope from a list of ropes UNTIL list = NIL DO rope _ Rope.Cat[list.first, ", ", rope]; list _ list.rest ENDLOOP}; END.  PWRouteImpl.mesa Copyright c 1985, 1986, 1987 by Xerox Corporation. All rights reversed. Last Edited by: Preas, February 5, 1987 9:01:34 pm PST -- AbutRoute: uses a channel router to connect adjacent objects -- Given two cells that we plan to abut, this module parses the corresponding edges, extract the pins, build nets according to the names on the pins, then calls the router to produce a cell containing the channel routing. -- Gets all the pins on the edge; pack them into nets and put in the SymTab.Ref [inst: CD.Instance] RETURNS [quit: BOOL _ FALSE] -- Group pins in net and insert in table -- Read the table and ship the nets to the channel router -- Make the design rules for proper spacing in this techno -- Initialize the channel: no fancy option for now -- In prevision of the use of the IRect coord system, origins are [0, 0] -- Parse the objects, get the pins, make the nets, put in table -- Read the table and ship the nets to the channel router -- Now route! [inst: CD.Instance] RETURNS [quit: BOOL _ FALSE] -- when PW is fixed clean this up [inst: Instance] RETURNS [obj: Object] -- when PW is fixed clean this up -- when PW is fixed clean this up XferPinsProc: TYPE = PROC [technologyKey: ATOM, inst: CD.Instance, routingLayer: CD.Layer, width: INT] RETURNS [cell: CD.Object] -- ΚT˜– "Cedar" stylešœ™Jšœ Οmœ=™HJšœ3Οk™6—J™šž œ˜ JšžœMžœ;˜Œ—J˜šΟn œžœžœ˜JšžœžœMžœ1˜‹Jšžœ ˜Jšžœžœ ˜J˜šΟc?™?šŸ œžœ\žœ˜ŠJšžœžœ ˜Jšž˜Jšœžœ˜"Jšœ5žœ˜?J˜Jš žœ žœžœžœžœ˜!Jšœ*˜*Jšœ žœžœ˜šžœ%žœžœž˜8Jšœ˜Jšžœžœžœžœ˜3šžœ*˜.Jšœ*˜*—Jšžœžœžœžœ˜7šžœ.˜2Jšœ.˜.—JšœBžœ˜_Jšœ žœžœ˜3Jšœ  ˜#Jšžœ˜—Jšœ žœ˜$J˜Jšžœžœžœ˜*Jšžœžœ˜$Jšžœ˜—J˜šŸœžœžœ˜Jšœ˜Jšœ žœ˜%Jšœžœ˜"Jšœ+˜+Jšžœžœ ˜Jšœ>žœ ˜N—J˜šŸœžœžœ˜Jšœ˜Jšœžœ˜#Jšœžœ˜$Jšœ+˜+Jšžœžœ ˜Jšœ>žœ ˜O—J˜šŸ œžœžœ˜Jšœ"žœ žœ˜3J˜Jšœ,žœžœ ˜Hšžœžœ˜Jšœ4žœ žœ ˜R—šž˜Jšœ4žœ žœ˜S———J˜Jš έ™έJšœžœžœ˜šœžœžœ˜Jšœ žœžœ  ˜&Jšœžœ ˜<—J˜Jšœžœžœ&˜TJ˜Jš O™OJšŸ œžœžœF˜]˜•StartOfExpansion4 -- [inst: CD.Instance] RETURNS [quit: BOOL _ FALSE]šΠbnœ&˜-Jš œžœ žœžœžœ™0Jšžœ%žœ$˜OJšœ˜—Jšžœžœžœ-˜>Jšœ˜—J˜code–[]šŸœžœžœ˜Lšœ žœž˜K˜ Kšœ˜Kšœ ˜ Kšœ˜Kšžœžœ˜—K˜—K˜šŸ œžœžœ˜Ešœ žœž˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšžœžœ˜—K˜—J™Jš (™(šŸ œžœžœ7˜aKšœžœ˜ Kšœžœ˜ K˜ K˜KšœH˜HKšœžœ#˜5Kš œžœžœžœžœ žœ-˜qJšœ,˜,Jš œžœžœžœžœžœ˜>Jšœ žœ˜Jšœ&˜&Jšœ˜—J˜Jš 9™9šŸœžœ>žœ˜kJ˜šŸ œ˜$Jšž˜Jšœžœžœ˜!Jšœ žœ˜Kš œžœžœžœžœ0˜mJšœ5žœ˜VJšœ>˜>Jšžœžœ˜Jšžœ˜—K˜Jšœ$˜$Kšœ˜K˜—š Ÿœžœžœ.žœ$žœ˜ƒKšžœ"˜)Jšœ˜Jšœ 7˜VJšœžœ˜6Jšœžœ ˜*Jšœ žœ˜J˜Jš :™:šœ˜šžœžœ  ˜šœ.˜.JšœB˜BJšœ@˜@Jšœ ˜ ——šžœ  ˜šœ.˜.JšœA˜AJšœB˜BJšœ ˜ ———Jš 2™2JšœK˜KJš H™HJšžœžœžœžœ˜.Jšžœžœžœžœ˜.Jšžœžœžœžœ˜EJšžœžœžœžœ˜AJ˜šžœžœ ˜Jšœ!˜!Jšœ˜Jšœ žœ ˜.Jšœ žœ'˜4Jšœ@˜@JšœA˜AJšœD˜DJšœA˜AJšœ˜J˜—šžœ ˜Jšœ˜Jšœ˜Jšœ žœ$˜2Jšœ žœ#˜0JšœB˜BJšœ?˜?JšœB˜BJšœC˜CJšœ˜—J™Jšœ#žœ ˜BJ™Jš ?™?Jšœžœžœžœ˜9Jšœžœžœžœ˜;Jšœžœžœžœ˜DJšœ žœžœžœ˜DJ™Jš 9™9Jšœ.˜.J˜Jš  ™ Jšœ-˜-Jšœ'˜'šžœ ž˜Jšœ’˜’JšœΘ˜ΘJšžœ˜—Jšžœžœžœžœi˜¨Jšžœžœžœžœr˜³K˜—š Ÿ œžœžœ7žœ.žœ žœ ˜ŸJ˜J˜JšœOžœ˜lJšœ#˜#K˜—šŸ œžœžœ.žœ;žœžœ žœ ˜ΏJšœi˜iJšœ;˜;Jšœ#˜#K˜—J™JšŸœžœžœ˜2J˜šŸœžœžœžœ žœ)žœžœYžœžœ žœ˜όJ–4 -- [inst: CD.Instance] RETURNS [quit: BOOL _ FALSE]˜š‘œ&˜.Jš œžœ žœžœžœ™0šžœ-žœ˜5Jšœ žœ*˜6šžœžœ˜!Jš !™!JšœžœI˜RJšœ žœ˜Jšœ,˜,Jšœ>˜>Jšœ žœ˜Jšœžœ˜)———J˜šŸœ˜#Jšœžœ™&Jšœžœ˜ šžœ/žœ˜7Jšœ žœ ˜Jšœ žœ ˜Jšœ žœ˜Jš !™!JšœžœI˜RJšœD˜DJšœ?˜?JšœX˜XJšœ-˜-Jšœ ˜ JšœY˜YJšœ,˜,JšœD˜DJšœ2˜2Jšœ˜J˜—Jšœ˜J˜—Jšœžœžœ˜Jšœ žœ ˜Jšœžœ@˜PJš !™!Jš œ žœžœ žœžœ žœ  ,˜‡šžœ žœžœ˜Jšœ2˜2šžœ ž˜JšœN˜N—J˜—J˜—J˜š Ÿœžœžœ žœžœ ˜;Jšœžœ˜3—J˜šœžœ˜(Jšœžœžœžœžœžœžœžœžœ  ™ƒJšœ žœ*˜6Jšœ žœ7˜DJšœ žœ8˜FJšœ žœ9˜HJšœ žœžœa˜sJšœžœ˜Jšœ žœžœ&˜8Jšœ žœX˜cJšœ˜Jšœ!˜!J˜šžœžœž˜šœ˜Jšœ˜šžœ žœ˜JšœZ˜ZJ˜——šœ˜Jšœ žœ7˜CJšœ žœ/˜>Jšœžœ˜Jšœ ˜ Jšœ9˜9Jšœ.˜.Jšœ]žœ˜bJšœz˜zšžœžœ˜$Jšœžœ9˜AJšœ žœ+˜6Jšœžœ?˜IJšœ žœ˜JšœD˜DJšœ˜JšœYžœ˜^Jšœt˜tJšœ˜—šžœžœ˜JšœžœF˜PJšœD˜DJšœ˜——J˜šœ˜šžœžœ˜$Jšœžœ9˜AJšœ žœ+˜6Jšœžœ˜Jšœ˜Jšœ:˜:Jšœ.˜.JšœYžœ˜^Jšœu˜ušžœžœ˜JšœžœE˜OJšœD˜DJšœ˜——šžœ˜Jšœ žœ7˜CJšœ žœ/˜>Jšœžœ˜Jšœ ˜ Jšœ:˜:Jšœ.˜.Jšœ]žœ˜bJšœ”˜”šžœžœ˜JšœžœC˜LJšœC˜CJšœ˜———J˜šœ˜Jšœžœ9˜AJšœ žœ+˜6Jšœžœ˜Jšœ˜Jšœ<˜JšœžœN˜WJšœ žœ[žœ˜mJšœC˜CJšœ˜Jšœx˜xJšœ˜—šžœžœ˜JšœžœF˜PJšœD˜DJšœ˜—J˜—Jšžœ˜—Jšœ žœ+˜N—J˜šŸ œžœžœ˜JK˜#Kšžœžœžœ;žœ˜WK˜—Jšžœ˜——…—=άRP