DIRECTORY CardTab, CD, CDRects, CDRoutingObjects, CDSimpleRules, CMosB, Core, CoreClasses, CoreOps, CoreRoute, DP, IO, RefTab, Rope, TerminalIO; DPSBox: CEDAR PROGRAM IMPORTS CardTab, CD, CDRects, CDRoutingObjects, CDSimpleRules, CMosB, CoreOps, CoreRoute, DP, IO, RefTab, Rope, TerminalIO EXPORTS DP = BEGIN Signal: SIGNAL = CODE; Node: TYPE = REF NodeRec; NodeRec: TYPE = RECORD[ name: DP.ROPE, minX: INT, maxX: INT, type: CD.Layer, row: INT _ -1, -- non assigned clouds: LIST OF Node _ NIL, rocks: LIST OF Node _ NIL, top: LIST OF INT_NIL, bot: LIST OF INT_NIL ]; PO: TYPE = CDRoutingObjects.PlacedObject; POList: TYPE = LIST OF PO; RNode: TYPE = CDRoutingObjects.Node; layRules: ATOM _ $cmosB; pwrSep: INT _ 1; AddSBNode: PUBLIC PROC[dp: DP.DataPath, row: INT, wire: DP.Wire, lc: DP.LayoutCoord] = { data: CoreClasses.RecordCellType _ NARROW[dp.cell.data]; rowW: INT _ dp.spec.n*dp.spec.cols*(dp.spec.chans + dp.spec.dChans)*DP.layChanW; range: INT _ IF wire.size#0 THEN wire.size ELSE IF lc.hor THEN 1 ELSE dp.spec.n; left: INT _ DP.metW/2-DP.leftTail; right: INT _ DP.metW/2-DP.leftTail + rowW; error: BOOL _ (IF NOT row IN [lc.rowBot..lc.rowTop] THEN ERROR ELSE FALSE); onLt: BOOL _ lc.hor AND lc.rowTop=row AND lc.sides[left]; onRt: BOOL _ lc.hor AND lc.rowTop=row AND lc.sides[right]; onTop: BOOL _ ~lc.hor AND (lc.rowTop>row OR lc.yTop=DP.initialYSize); onBot: BOOL _ ~lc.hor AND (lc.rowBot node.maxX THEN Signal[]; IF (node.maxX - node.minX) IN (0..DP.lambda*2) THEN Signal[]; ENDLOOP }; InsertPos: PROC[pos: INT, origList: LIST OF INT] RETURNS[LIST OF INT] = { origList _ CONS[0, origList]; FOR list: LIST OF INT _ origList, list.rest DO IF list.rest#NIL AND list.rest.first = pos THEN EXIT; IF list.rest=NIL OR list.rest.first > pos THEN {list.rest _ CONS[pos, list.rest]; EXIT}; ENDLOOP; RETURN[origList.rest]}; BuildSB: PUBLIC PROC[dp: DP.DataPath, row: INT] = { cellWidth: INT _ DP.BitWidth[dp]; range: INT _ dp.spec.n*dp.spec.cols; left: INT _ DP.metW/2-DP.leftTail; right: INT _ DP.metW/2-DP.leftTail+range*cellWidth; FilterNulls[dp, row]; AddPowerPassChans[dp, row]; MarkPolys[dp[row].sb, dp.spec.sbPolyBits*cellWidth, left, right]; dp[row].obj _ ThreeLevelRoute[dp[row].sb, range, left, right, cellWidth, dp.spec.chans]; dp[row].layY.size _ CD.InterestSize[dp[row].obj].y}; AddPowerPassChans: PROC[dp: DP.DataPath, row: INT] = { cellWidth: INT _ (dp.spec.chans + dp.spec.dChans)*DP.layChanW; left: INT _ DP.metW/2-DP.leftTail; right: INT _ DP.metW/2-DP.leftTail+dp.spec.n*dp.spec.cols*cellWidth; AddPassChans: PROC[name: DP.ROPE, chan: INT] = { refNode: Node _ GetNamedNode[dp[row].sb, name]; IF refNode#NIL THEN {refNode.minX _ left; refNode.maxX _ right}; FOR col: INT IN [0..dp.spec.cols) DO FOR bit: INT IN [0..dp.spec.n) DO coord: INT _ DP.BitPosX[dp, col, bit] + (chan-1)*DP.layChanW/2; node: Node _ refNode; IF node=NIL THEN { nm: DP.ROPE _ IO.PutFR["%g%g", IO.rope[name], IO.int[col*dp.spec.n+bit]]; node _ NEW[NodeRec _ [name: nm, minX: coord, maxX: coord, type: CMosB.met2]]; [ ] _ RefTab.Store[dp[row].sb, CoreOps.CreateWires[0], node]}; node.top _ InsertPos [coord, node.top]; node.bot _ InsertPos [coord, node.bot]; ENDLOOP ENDLOOP}; AddPassChans["Gnd", (dp.spec.chans+1 )*2-1]; AddPassChans["Vdd", (dp.spec.chans+1+ pwrSep)*2-1]}; GetNamedNode: PROC[symTab: RefTab.Ref, name: DP.ROPE] RETURNS[node: Node] = { found: BOOL _ FALSE; filter: RefTab.EachPairAction = { node _ NARROW[val]; IF node.name.Equal[name] THEN {found _ TRUE; RETURN[TRUE]}}; [ ] _ RefTab.Pairs[symTab, filter]; IF NOT found THEN node _ NIL}; FilterNulls: PROC[dp: DP.DataPath, row: INT] = { filter: RefTab.EachPairAction = { node: Node _ NARROW[val]; IF (node.top=NIL OR node.bot=NIL) AND (node.minX=node.maxX) AND (NOT node.name.Equal["Vdd"]) AND (NOT node.name.Equal["Gnd"]) THEN { TerminalIO.PutF["Deleting row %g SB node: %g\n", IO.int[row], IO.rope[node.name]]; []_RefTab.Delete[dp[row].sb, key]} }; [ ] _ RefTab.Pairs[dp[row].sb, filter]}; MarkPolys: PROC[symTab: RefTab.Ref, polyMax, min, max: INT] = { mark: RefTab.EachPairAction = { node: Node _ NARROW[val]; node.type _ IF node.maxX-node.minX <= polyMax AND node.maxX < max AND node.minX > min THEN CMosB.pol ELSE CMosB.met2; RETURN[FALSE] }; IF polyMax<1 THEN RETURN; [ ] _ RefTab.Pairs[symTab, mark]}; topTail: INT _ 3*DP.lambda; -- DP.topTail botTail: INT _ 3*DP.lambda; -- DP.botTail botBias: INT _ DP.cnctSize/2 + botTail; GetSBY: PUBLIC PROC[sb: RefTab.Ref, wire: DP.Wire] RETURNS[loc: INT, layer: CD.Layer] = { node: Node _ NARROW[RefTab.Fetch[sb, wire].val]; RETURN[botBias + node.row, node.type]}; ThreeLevelRoute: PROC [ nodeTable: RefTab.Ref, range, left, right, cellWidth, channels: INT] RETURNS[obj: CD.Object] = { ColWidth: PROC[xx: INT] RETURNS[INT] ~ {RETURN[ SELECT (xx MOD cellWidth)/ DP.layChanW FROM channels, channels+pwrSep => DP.pwrW, ENDCASE => DP.metW]}; pitch: CD.Position _ [DP.layChanW, DP.layChanW/2]; -- y pitch - met2 pol met2 pol . . . pass: LIST OF Node _ NIL; conn: LIST OF Node _ NIL; assigned: LIST OF Node _ NIL; iSize: CD.Rect _ [left, 0, right, 0]; rNodes: LIST OF CDRoutingObjects.Node _ NIL; MarkNodeDependencies[nodeTable]; [pass, conn] _ BuildNodeLists[nodeTable, iSize]; DO progress: BOOL _ FALSE; workToDo: BOOL _ FALSE; FOR nodePtr: LIST OF Node _ conn, nodePtr.rest WHILE nodePtr#NIL DO polyRow: BOOL _ nodePtr.first.type = CMosB.pol; rockMax: INT _ pitch.y*(IF polyRow THEN -1 ELSE -2); IF nodePtr.first.row>-1 THEN LOOP; workToDo _ TRUE; FOR rocks: LIST OF Node _ nodePtr.first.rocks, rocks.rest WHILE rocks#NIL DO IF rocks.first.row=-1 THEN GOTO unAssignedRocks; rockMax _ MAX[rockMax, rocks.first.row]; REPEAT unAssignedRocks => LOOP ENDLOOP; progress _ TRUE; assigned _ AddUnassignedNodeToAssigned [polyRow, pitch.y, nodePtr.first, assigned, rockMax]; ENDLOOP; IF NOT workToDo THEN EXIT; IF NOT progress THEN Signal[]; ENDLOOP; iSize.y2 _ 0; FOR nodePtr: LIST OF Node _ assigned, nodePtr.rest WHILE nodePtr#NIL DO iSize.y2 _ MAX[iSize.y2, nodePtr.first.row] ENDLOOP; iSize.y2 _ iSize.y2 + DP.cnctSize/2 + topTail; iSize.y1 _ 0 - botBias; FOR nodePtr: LIST OF Node _ pass, nodePtr.rest WHILE nodePtr#NIL DO -- pass name: DP.ROPE _ nodePtr.first.name; wireW: INT _ ColWidth[nodePtr.first.minX]; xx: INT _ nodePtr.first.minX - (wireW-DP.metW)/2; IF nodePtr.first.top=NIL OR nodePtr.first.bot=NIL THEN { TerminalIO.PutF["\nSignal %g is not connected anywhere", IO.rope[nodePtr.first.name]]; Signal[]}; rNodes _ CONS[ CDRoutingObjects.CreateNode[ AddPO[NIL, [wireW, iSize.y2-iSize.y1], [xx, iSize.y1], CMosB.met]], rNodes]; ENDLOOP; rNodes _ DrawAssigndedNodes[rNodes, assigned, iSize, ColWidth]; obj _ CDRoutingObjects.CreateRoutingObject[rNodes, iSize]; RETURN[obj]}; Branch: TYPE = REF BranchRec; BranchRec: TYPE = RECORD[loc: INT, top, bot: Node]; InsertBranch: PROC [branchTab: CardTab.Ref, s: {top,bot}, loc: INT, node: Node] ~ { branch: Branch _ NARROW[CardTab.Fetch[branchTab, loc].val]; IF branch=NIL THEN { branch _ NEW[BranchRec _ [loc: loc]]; [ ] _ CardTab.Store[branchTab, loc, branch]}; SELECT s FROM top => {IF branch.top#NIL THEN Signal[]; branch.top _ node}; bot => {IF branch.bot#NIL THEN Signal[]; branch.bot _ node}; ENDCASE}; MarkNodeDependencies: PROC[nodeTable: RefTab.Ref] = { InsertNodes: RefTab.EachPairAction = { node: Node _ NARROW[val]; FOR list: LIST OF INT _ node.top, list.rest WHILE list#NIL DO InsertBranch[branchTab, top, list.first, node] ENDLOOP; FOR list: LIST OF INT _ node.bot, list.rest WHILE list#NIL DO InsertBranch[branchTab, bot, list.first, node] ENDLOOP; RETURN[FALSE] }; MarkNodes: CardTab.EachPairAction = { branch: Branch _ NARROW[val]; IF branch.top=NIL OR branch.bot=NIL OR branch.top=branch.bot THEN RETURN[FALSE]; FOR list: LIST OF Node _ branch.top.rocks, list.rest WHILE list#NIL DO IF list.first=branch.bot THEN EXIT; REPEAT FINISHED => branch.top.rocks _ CONS[branch.bot, branch.top.rocks] ENDLOOP; FOR list: LIST OF Node _ branch.bot.clouds, list.rest WHILE list#NIL DO IF list.first=branch.top THEN EXIT; REPEAT FINISHED => branch.bot.clouds _ CONS[branch.top, branch.bot.clouds] ENDLOOP; RETURN[FALSE] }; branchTab: CardTab.Ref _ CardTab.Create[]; [ ] _ RefTab.Pairs[nodeTable, InsertNodes]; [ ] _ CardTab.Pairs[branchTab, MarkNodes]}; BuildNodeLists: PROC[nodeTable: RefTab.Ref, iSize: CD.Rect] RETURNS[pass, conn: LIST OF Node] = { SortNodes: RefTab.EachPairAction = { node: Node _ NARROW[val]; SELECT TRUE FROM node.maxX = node.minX => pass _ OrderedInsertion[pass, node]; ENDCASE => conn _ OrderedInsertion[conn, node]; RETURN[FALSE] }; [ ] _ RefTab.Pairs[nodeTable, SortNodes]}; OrderedInsertion: PROC[list: LIST OF Node, node: Node] RETURNS [LIST OF Node] = { NotOrdered: PROC[n0, n1: Node] RETURNS[BOOL] = { test: INT _ (n0.maxX-n0.minX)-(n1.maxX-n1.minX); -- largest segemet first IF test = 0 THEN test _ (n1.minX)-(n0.minX); -- smallest position first RETURN[test < 0]}; IF list = NIL THEN RETURN[LIST[node]]; IF NotOrdered[n0: list.first, n1: node] THEN RETURN[CONS[node, list]]; FOR ls: LIST OF Node _ list, ls.rest DO IF NotOrdered[n0: ls.first, n1: node] THEN {ls.rest _ CONS[ls.first, ls.rest]; ls.first _ node; RETURN[list]}; IF ls.rest = NIL THEN {ls.rest _ LIST[node]; RETURN[list]} ENDLOOP}; AddUnassignedNodeToAssigned: PROC [oddRow: BOOL, step: INT, node: Node, orig: LIST OF Node, rockMax: INT] RETURNS [LIST OF Node] = { Next: PROC[row: INT] RETURNS[next: INT] = { next _ row + (IF (((row/step+2) MOD 2)=1)=oddRow THEN 2*step ELSE step) }; minOkRow: INT _ Next[rockMax+step]; IF orig=NIL OR minOkRow < orig.first.row THEN {node.row _ minOkRow; RETURN[CONS[node, orig] ]}; IF minOkRow = orig.first.row AND node.maxX < orig.first.minX THEN {node.row _ minOkRow; RETURN[CONS[node, orig] ]}; FOR list: LIST OF Node _ orig, list.rest DO thisRow: INT _ list.first.row; nextRow: INT _ IF list.rest#NIL THEN list.rest.first.row ELSE Next[MAX[thisRow, rockMax+step]]; nextMinX: INT _ IF list.rest#NIL THEN list.rest.first.minX ELSE LAST[INT]; minOkRow _ Next[MAX[thisRow-step, rockMax+step]]; -- thisRow or thisRow+step SELECT TRUE FROM thisRow = nextRow => SELECT TRUE FROM minOkRow < thisRow => ERROR; minOkRow > thisRow => LOOP; list.first.maxX >= node.minX => LOOP; node.maxX >= nextMinX => LOOP; ENDCASE => node.row _ thisRow; thisRow+step = nextRow => SELECT TRUE FROM minOkRow < thisRow => ERROR; minOkRow = thisRow => SELECT TRUE FROM list.first.maxX >= node.minX => LOOP; ENDCASE => node.row _ thisRow; minOkRow = nextRow => SELECT TRUE FROM node.maxX >= nextMinX => LOOP; ENDCASE => node.row _ nextRow; minOkRow > thisRow => LOOP; ENDCASE => ERROR; thisRow+2*step = nextRow, thisRow+3*step = nextRow => SELECT TRUE FROM minOkRow < thisRow => ERROR; minOkRow = thisRow => SELECT TRUE FROM list.first.maxX < node.minX => node.row _ thisRow; node.maxX < nextMinX => node.row _ nextRow; ENDCASE => LOOP; minOkRow < nextRow => node.row _ minOkRow; -- between minOkRow = nextRow => SELECT TRUE FROM node.maxX < nextMinX => node.row _ nextRow; ENDCASE => LOOP; ENDCASE => LOOP; ENDCASE => ERROR; list.rest _ CONS[node, list.rest]; IF oddRow#(((node.row)/step MOD 2)=1) THEN Signal[]; EXIT ENDLOOP; RETURN[orig]}; AddPO: PROC[poList: POList, size: CD.Position, loc: CD.Position, layer: CD.Layer] RETURNS[POList] = { IF size.x<(DP.lambda*2) OR size.y<(DP.lambda*2) THEN ERROR; IF size.x=0 OR size.y=0 THEN RETURN[poList]; poList _ CONS[ [CDRects.CreateRect[size, layer], loc], poList]; RETURN[poList]}; DrawAssigndedNodes: PROC[rNodes: LIST OF RNode, assigned: LIST OF Node, iSize: CD.Rect, ColWidth: PROC[xx: INT] RETURNS[INT]] RETURNS[LIST OF RNode] = { FOR nodePtr: LIST OF Node _ assigned, nodePtr.rest WHILE nodePtr#NIL DO -- top bot name: DP.ROPE _ nodePtr.first.name; yMid: INT _ nodePtr.first.row; minX: INT _ nodePtr.first.minX; maxX: INT _ nodePtr.first.maxX; rowType: CD.Layer _ nodePtr.first.type; rowWW: INT _ IF rowType = CMosB.pol THEN DP.polW ELSE DP.met2W; ctct: CD.Object _ CDSimpleRules.Contact[layRules, CMosB.met, rowType]; pos: LIST OF PO _ NIL; pos _ AddPO[pos, [maxX-minX, rowWW], [minX, yMid-rowWW/2], rowType]; FOR nodeTop: LIST OF INT _ nodePtr.first.top, nodeTop.rest WHILE nodeTop#NIL DO colW: INT _ ColWidth[nodeTop.first]; xx: INT _ nodeTop.first - (colW-DP.metW)/2; pos _ AddPO[pos, [colW, iSize.y2-yMid], [xx, yMid], CMosB.met]; pos _ CONS[ [ctct, [xx-(DP.cnctSize-colW)/2, yMid-DP.cnctSize/2]], pos]; ENDLOOP; FOR nodeBot: LIST OF INT _ nodePtr.first.bot, nodeBot.rest WHILE nodeBot#NIL DO colW: INT _ ColWidth[nodeBot.first]; xx: INT _ nodeBot.first - (colW-DP.metW)/2; pos _ AddPO[pos, [colW, yMid-iSize.y1], [xx, iSize.y1], CMosB.met]; pos _ CONS[ [ctct, [xx-(DP.cnctSize-colW)/2, yMid-DP.cnctSize/2]], pos]; ENDLOOP; rNodes _ CONS[CDRoutingObjects.CreateNode[pos], rNodes]; ENDLOOP; RETURN[rNodes]}; END. ΊDPSBox.mesa, Copyright c 1986 by Xerox Corporation. All rights reserved. Don Curry May 5, 1987 10:20:26 am PDT Last Edited by: Don Curry November 16, 1987 6:16:02 pm PST row is assumed to be IN [lc.rowBot..lc.rowTop]; IF NOT lc.hor AND wire.size#0 AND wire.size#dp.spec.n AND (lc.yTop#DP.initialYSize/2 OR lc.yBot#DP.initialYSize/2) THEN Signal[]; Will cause horizontal channel to be allocated only if pwr node gets used as signal Otherwise, independent pass nodes are allocated named Gnd[0], Gnd[1], ... These will end up as pass nodes. Assign Node Rows IF polyRow THEN Signal[]; Switchbox can't be built without additional branches. Try shifting top or bottom signals to remove conflicts. Determine Y Size Draw extension wires IsPwr: PROC[name: DP.ROPE] RETURNS[BOOL] = { first3: DP.ROPE _ name.Substr[0,3]; RETURN[Rope.Equal[first3,"Gnd"] OR Rope.Equal[first3,"Vdd"]]}; AddRet: PROC[insts: CD.InstanceList, size: CD.Position, loc: CD.Position, layer: CD.Layer] RETURNS[CD.InstanceList] = { IF size.x<(DP.lambda*2) OR size.y<(DP.lambda*2) THEN ERROR; IF size.x=0 OR size.y=0 THEN RETURN[insts]; insts _ CONS[ NEW[CD.InstanceRep _ [CDRects.CreateRect[size, layer], [loc]]], insts]; RETURN[insts]}; Κϋ˜šœ ™ Jšœ<™Jšœœœœ ˜$Jšœ œœœ+˜Fš ž œœœœœ˜0Jšœ0˜0Jšœ œœ-˜@šœœœ˜$šœœœ˜!Jšœœœ"œ ˜?Jšœ˜šœœœ˜Jš œœœœœ œ˜IJšœœC˜MJšœ>˜>—Jšœ(˜(Jšœ(˜(Jšœœ˜———Jšœ.˜.Jšœ4˜4—J˜š ž œœœœœ˜MJšœœœ˜šœ!˜!Jšœœ˜Jš œœ œœœ˜<—Jšœ#˜#Jšœœœœ˜—J˜šž œœœœ˜0šœ!˜!Jšœ œ˜š œ œœ œ˜%Jšœ˜Jšœœ˜ šœœœ˜#Jšœ1œ œ˜RJšœ%˜%———Jšœ(˜(—J˜šž œœ(œ˜?šœ˜Jšœ œ˜šœ ˜Jšœ˜"Jšœ˜šœ˜Jšœ œ ˜——Jšœœ˜—Jšœ œœ˜Jšœ"˜"—J˜Jšœ œœ Ÿ ˜)Jšœ œœ Ÿ ˜)Jšœ œœ˜'J˜šžœœœœ˜2Jšœœ œ ˜&Jšœœ˜2Jšœ!˜'—J˜šžœ˜šœBœ˜FJšœœ ˜—š žœœœœœœ˜/šœœ œ ˜,Jšœœœœ˜;——Jšœœ œ œŸ$˜WJšœœœœ˜Jšœœœœ˜Jšœ œœœ˜Jšœœ˜&Jšœœœœ˜,Jšœ ˜ Jšœ0˜0Jšœ™š˜Jšœ œœ˜Jšœ œœ˜š œ œœœ œ˜CJšœ œ"˜/Jš œ œ œ œœ˜4Jšœ œ ™Jšœœœ˜"Jšœ œ˜š œœœ(œœ˜LJšœœœ˜0Jšœ œ˜(Jšœœœ˜'—Jšœ œ˜šœ&˜&Jšœ5˜5—Jšœ˜—Jšœœ œœ˜šœœ œ ˜JšΟbm™m—Jšœ˜—Jš ™Jšœ ˜ š œ œœœ œ˜GJšœ œœ˜4—Jšœœ˜.Jšœ˜Jš ™š œ œœœ œœŸ˜KJšœœœ˜$Jšœœ!˜+Jšœœ œ ˜4š œœœœœ˜8Jšœ9œ˜VJ˜ —šœ œ˜+JšœœC˜L—Jšœ˜—Jšœ?˜?Jšœ:˜:Jšœ˜ J˜—Jšœ œœ ˜Jšœ œœœ˜3J˜šž œœ-œ˜SJšœœ$˜;šœœœ˜Jšœ œ˜%Jšœ-˜-—šœ˜ Jšœœ œœ˜˜>Jšœ.˜5—Jšœœ˜—Jšœ*˜*—šžœœœœœœœ ˜Qšž œœœœ˜0Jšœœ(Ÿ˜IJšœ œŸ˜HJšœ ˜—Jš œœœœœ˜&Jš œž œœœœ˜Fšœœœ˜'šœ$˜*Jšœ œ&œ˜C—Jš œ œœ œœœ˜D——šžœ˜!Jš œ œœœœœ˜GJšœœœ ˜š žœœœœœ˜*Jš œœœœœ ˜L—Jšœ œ˜#šœœœ˜)Jšœœœ˜6—šœœ˜=Jšœœœ˜6—šœœœ˜+Jšœ œ˜šœ œœ ˜Jšœ˜Jšœœ˜&—šœ œœ ˜ Jšœ˜Jšœœœ˜—JšœœŸ˜Lšœœ˜šœœœ˜)Jšœœ˜ Jšœœ˜Jšœ œ˜%Jšœœ˜ Jšœ˜%—šœœœ˜,Jšœœ˜šœœœ˜)Jšœ œ˜%Jšœ˜%—šœœœ˜)Jšœœ˜ Jšœ˜%—Jšœœ˜Jšœ œ˜—Jšœ˜šœœœ˜.Jšœœ˜šœœœ˜)Jšœ3˜3Jšœ-˜-Jšœ œ˜—Jšœ.Ÿ ˜8šœœœ˜)Jšœ-˜-Jšœ œ˜—Jšœ œ˜—Jšœ œ˜—Jšœ œ˜"Jšœœœ ˜4Jšœœ˜ —Jšœ˜—J˜š žœœœœœœ™,Jšœœœ™#Jšœœ™>—J˜š žœœœœœœ™ZJšœœ™Jš œ œ œ œ œœ™;Jšœ œ œœ™+JšœœœœA™UJšœ ™—š žœœœœœ˜QJšœ ˜Jš œ œ œ œ œœ˜;Jšœ œ œœ ˜,Jšœ œ2˜?Jšœ ˜—J˜šžœœ œœœœœ˜Wš žœœœœœ˜%Jšœœœ ˜—š œ œœœ œœŸ ˜RJšœœœ˜$Jšœœ˜ Jšœœ˜!Jšœœ˜!Jšœ œ˜'Jš œœœœœœœ˜@Jšœœ>˜GJš œœœœœ˜JšœD˜Dš œ œœœ#œ œ˜OJšœœ˜%Jšœœœ ˜-JšœC˜CJšœœœœ˜HJšœ˜—š œ œœœ#œ œ˜OJšœœ˜%Jšœœœ ˜-JšœE˜EJšœœœœ˜HJšœ˜—Jšœ œ+˜8Jšœ˜—Jšœ ˜—Jšœ˜J˜——…—7€Q5