DIRECTORY BasicTime, CD, CDOrient, CDBasics, Core, CoreBlock, CoreFrame, CoreGlue, CoreName, CoreOps, CoreProperties, FS, IO, PWCLayoutCheckpoint, PWC, Rope, TypeScript, ViewerIO, WatchStats; CoreFrameImpl: CEDAR PROGRAM IMPORTS BasicTime, CDOrient, CDBasics, CoreBlock, CoreFrame, CoreGlue, CoreName, CoreOps, CoreProperties, FS, IO, PWCLayoutCheckpoint, PWC, Rope, TypeScript, ViewerIO, WatchStats EXPORTS CoreFrame = BEGIN OPEN CoreFrame; Signal: SIGNAL = CODE; frameCellClass: PUBLIC Core.CellClass _ CoreOps.SetClassPrintProc[ class: NEW[ Core.CellClassRec _ [name: "Frame", recast: RecastFrameSoft]], proc: ClassPrintProc]; ClassPrintProc: CoreOps.PrintClassProc = { frame: Frame _ NARROW [data]; LogFrameData[frame, 0, TRUE, out]; FOR ii: INT IN [0..frame.seq.size) DO LogFCT[frame.seq[ii], 1, 21, out] ENDLOOP}; FreeRope: ARRAY Freedom OF ROPE _ ["fixed", "tame", "wild"]; SideRope: PUBLIC ARRAY Side OF ROPE _ ["BOTTOM", "RIGHT", "TOP", "LEFT"]; LogFCT: PUBLIC PROC[frameCT: CT, indent: INT, lim: INT, out: IO.STREAM _ NIL] = { name: ROPE _ CoreName.CellNm[frameCT].n; gap: INT _ MAX[42 - 2*indent - name.Length[], 2]; frame: Frame; IF out=NIL THEN out _ GetLog[]; IF frameCT.class#frameCellClass THEN {CoreOps.PrintCellType[frameCT]; RETURN}; frame _ FCT[frameCT]; CoreOps.PrintIndent[indent, out]; out.PutRope[name]; FOR ii: INT IN [0..gap) DO out.PutChar[IO.SP] ENDLOOP; LogFrameData[frame, 0, FALSE, out]; IF indent {cached _ FALSE; CONTINUE}; file: FS.OpenFile _ FS.Open[ name.Cat["Shell", ".dale"] ]; IF cached THEN FS.Close[file] ELSE log.PutF["\n%g is not cached", IO.rope[name]]}; ReadFrameCache: PUBLIC PROC[name: ROPE] RETURNS[frameCT: CT] = { count: INT _ 0; Period: TYPE = {begin, mid, end}; time: ARRAY Period OF BasicTime.GMT; vm: ARRAY Period OF INT; cell: CT; cached: BOOL _ TRUE; time[begin] _ BasicTime.Now[]; vm[begin] _ WatchStats.GetWatchStats[].vmFree; IF NOT CellCached[name] THEN RETURN[NIL]; cell _ RetrieveAndDecorate[name]; frameCT _ NewFrameCell[size: 0, name: name, rec: [cell: cell] ]; []_CoreName.CellNm[frameCT, CoreName.CellNm[cell].n]; time[mid] _ BasicTime.Now[]; vm[mid] _ WatchStats.GetWatchStats[].vmFree; count _ PWC.CountTransistors[cell]; time[end] _ BasicTime.Now[]; vm[end] _ WatchStats.GetWatchStats[].vmFree; log.PutF["\nReadFrameCache: %g %g", IO.rope[name], IO.time[]]; log.PutF["\n Begin: vm: %6g %g", IO.int[vm[begin]], IO.time[time[begin]]]; log.PutF["\n Core: vm: %6g %g", IO.int[vm[mid]], IO.time[time[mid]]]; log.PutF["\n Dale: vm: %6g %g", IO.int[vm[end]], IO.time[time[end]]]; log.PutF["\n Cost: vm: %6g", IO.int[ vm[begin]-vm[end] ] ]; log.PutF["\n Xstrs: %g", IO.int[ count ] ]}; WriteFrameCache: PUBLIC PROC[frameCT: CT] = { Period: TYPE = {begin, mid, end}; time: ARRAY Period OF BasicTime.GMT; vm: ARRAY Period OF INT; count: INT; name: ROPE _ CoreName.CellNm[frameCT].n; IF name.Length[]=0 THEN Signal[]; time[begin] _ BasicTime.Now[]; vm[begin] _ WatchStats.GetWatchStats[].vmFree; [ ] _ Expand[hard, frameCT]; [ ] _ CoreGlue.RouteHard[frameCT]; [ ] _ CoreName.CellNm[FCT[frameCT].cell, name]; PWCLayoutCheckpoint.Store[FCT[frameCT].cell]; FCT[frameCT].cell _ RetrieveAndDecorate[name]; count _ PWC.CountTransistors[FCT[frameCT].cell]; FCT[frameCT].seq _ NEW[FrameSeq[0]]; time[mid] _ BasicTime.Now[]; vm[mid] _ WatchStats.GetWatchStats[].vmFree; time[end] _ BasicTime.Now[]; vm[end] _ WatchStats.GetWatchStats[].vmFree; log.PutF["\nWriteFrameCache: %g %g", IO.rope[name], IO.time[]]; log.PutF["\n Start: vm: %6g %g", IO.int[vm[begin]], IO.time[time[begin]]]; log.PutF["\n Built: vm: %6g %g", IO.int[vm[mid]], IO.time[time[mid]]]; log.PutF["\n Cached: vm: %6g %g", IO.int[vm[end]], IO.time[time[end]]]; log.PutF["\n Cost: vm: %6g", IO.int[ vm[begin]-vm[end] ] ]; log.PutF["\n Xstrs: %g", IO.int[ count ] ]}; RetrieveAndDecorate: PUBLIC PROC[name: ROPE] RETURNS[cell: CT] = { cell _ PWCLayoutCheckpoint.Retrieve[name]; CoreBlock.MarkSides[cell]}; SetFrameExpandProc: PUBLIC PROC[type: ExpandType, on: REF, proc: REF ExpandProc] = { expandProc: ATOM _ IF type=soft THEN frameExpandSoftProc ELSE frameExpandHardProc; WITH on SELECT FROM cell: Core.CellType => {cell.properties _ CoreProperties.PutProp[cell.properties, expandProc, proc]}; class: Core.CellClass => {class.properties _ CoreProperties.PutProp[class.properties, expandProc, proc]}; ENDCASE => Signal[]}; GetFrameExpandProc: PUBLIC PROC[type: ExpandType, on: REF] RETURNS[proc: REF ExpandProc] = { expandProc: ATOM _ IF type=soft THEN frameExpandSoftProc ELSE frameExpandHardProc; WITH on SELECT FROM cell: Core.CellType => {proc _ NARROW[ CoreProperties.GetCellTypeProp[cell, expandProc].value]}; class: Core.CellClass => {proc _ NARROW[ CoreProperties.GetCellClassProp[class, expandProc].value]}; ENDCASE => Signal[]}; RecastFrameSoft: PUBLIC Core.RecastProc = {Signal[]}; RecastFrameHard: PUBLIC Core.RecastProc = { frame: Frame _ FCT[me]; name: ROPE _ CoreName.CellNm[me].n; cells: LIST OF CT; ReOrderFrame[me]; IF frame.cell=NIL THEN { IF frame.seq.size=0 THEN {Signal[]; RETURN[new]}; -- NIL FOR i: INT DECREASING IN [0..frame.seq.size) DO cells _ CONS[RecastFrameHard[frame.seq[i]], cells] ENDLOOP; frame.cell _ CoreBlock.AbutCellList [name: name, first: SideSides[frame.first], cellTypes: cells]}; RETURN[frame.cell]}; RecastWithLayout: PUBLIC Core.RecastProc = { Signal[] }; ReOrderFrame: PROC[cell: Core.CellType] = { DualCell: TYPE = RECORD[cell0, cell1: Core.CellType]; frame: Frame; IF cell.class # frameCellClass THEN RETURN; frame _ FCT[cell]; SELECT frame.first FROM right => frame.first _ left; top => frame.first _ bottom; ENDCASE => RETURN; FOR i: INT IN [0..frame.seq.size/2) DO j: INT _ frame.seq.size-1-i; [frame.seq[j], frame.seq[i]] _ DualCell[frame.seq[i], frame.seq[j]] ENDLOOP}; Expand: PUBLIC PROC[type: ExpandType, frameCT: CT] = { expandNodeProc: EnumProc = { expand: REF ExpandProc _ NIL; IF expand=NIL THEN expand _ GetFrameExpandProc[type, fCT]; IF expand=NIL THEN expand _ GetFrameExpandProc[type, fCT.class]; IF expand#NIL THEN expand^[type, fCT]; IF fCT.class # frameCellClass THEN Signal[]; RETURN[kids: TRUE, continue: TRUE] }; checkNodeProc: EnumProc = { IF fCT.class # frameCellClass THEN Signal[]}; []_EnumFrame[frameCT, expandNodeProc]; []_EnumFrame[frameCT, checkNodeProc]}; NameFrame: PUBLIC PROC[cell: CT, root: ROPE _ NIL] = { name: ROPE _ CoreName.CellNm[cell].n; IF name=NIL THEN {name _ root; []_CoreName.CellNm[cell, name]}; IF name=NIL THEN RETURN; SELECT cell.class FROM frameCellClass => { frame: Frame _ FCT[cell]; IF frame.cell#NIL THEN NameFrame[frame.cell, name.Cat[".RC"]]; FOR field: INT IN [0..frame.seq.size) DO subName: ROPE _ IO.PutFR["%g[%g]", IO.rope[name], IO.int[field]]; NameFrame[frame.seq[field], subName]; ENDLOOP}; ENDCASE}; Fetch: PUBLIC PROC[cell: CT, subCellName: ROPE] RETURNS[subCell: CT _ NIL] = { fetchNodeProc: EnumProc = { IF CoreName.CellNm[fCT].n=subCellName THEN {subCell _ fCT; RETURN[FALSE, FALSE]} }; subCellName _ CoreName.RopeNm[subCellName]; []_EnumFrame[cell, fetchNodeProc] }; Log2: PROC [n: INT] RETURNS [INT] = {RETURN [IF n<=1 THEN 0 ELSE 1 + Log2[n / 2]]}; Exp2: PROC [n: INT] RETURNS [INT] = {RETURN [IF n<=0 THEN 1 ELSE 2 * Exp2[n-1]]}; RotateFrame: PUBLIC PROC[frameCT: CT, orient: CD.Orientation _ CD.original] = { ReSeq: PROC = { TwoFrames: TYPE = RECORD[f0, f1: CT]; FOR i: INT IN [0..frame.seq.size/2) DO [frame.seq[frame.seq.size-i-1], frame.seq[i]] _ TwoFrames[frame.seq[i], frame.seq[frame.seq.size-i-1]]; ENDLOOP}; frame: Frame _ FCT[frameCT]; orient _ CDOrient.ComposeOrient[frame.orient, orient]; frame.orient _ CDOrient.original; IF frame.cell#NIL THEN { -- In this case, child frames are ignored IF orient=CDOrient.original THEN RETURN; frame.cell _ RotateCellType[frame.cell, orient]; frame.size _ FixedSize[PWC.InterestSize[frame.cell]]; RETURN}; IF frame.seq.size = 0 THEN { -- Data must be glue IF orient=CDOrient.original THEN RETURN; IF frame.data=NIL THEN ERROR; WITH frame.data SELECT FROM glue: CoreGlue.Glue => { OPEN glue; srcs: ARRAY Side OF Side _ OrientSideSide[CDOrient.InverseOrient[orient]]; glue.type _ [type[srcs[bottom]], type[srcs[right]], type[srcs[top]], type[srcs[left]] ]; glue.cell _ [cell[srcs[bottom]], cell[srcs[right]], cell[srcs[top]], cell[srcs[left]] ]; FOR side: Side IN Side DO IF cell[side]#NIL AND type[side]=chan THEN cell[side] _ RotateCellType[cell[side], orient]; -- for chan cells ENDLOOP; SELECT orient FROM CDOrient.rotate90, CDOrient.rotate90X, CDOrient.rotate270, CDOrient.rotate270X => { RopePair: TYPE = RECORD[r0, r1: ROPE]; [params.branchLayer, params.trunkLayer] _ RopePair[ params.trunkLayer, params.branchLayer]; frame.size _ FlipSize[frame.size]; tDir _ IF tDir=horizontal THEN vertical ELSE horizontal}; ENDCASE }; ENDCASE => ERROR; RETURN}; frame.first _ OrientSideSide[orient][frame.first]; ReOrderFrame[frameCT]; -- first _ left or bottom FOR i: INT IN [0..frame.seq.size) DO RotateFrame[frame.seq[i], orient] ENDLOOP}; OrientSideSide: PUBLIC ARRAY CD.Orientation OF ARRAY Side OF Side _ [ [bottom, right, top, left], [bottom, left, top, right], [right, top, left, bottom], [left, top, right, bottom], [top, left, bottom, right], [top, right, bottom, left], [left, bottom, right, top], [right, bottom, left, top] ]; RotateCellType: PUBLIC PROC[cell: CT, orient: CD.Orientation] RETURNS[new: CT] = { OrientATOM: ARRAY CD.Orientation OF ATOM = [ -- to match style of PWC procs $Rot0, $Rot0X, $Rot90, $Rot90X, $Rot180, $Rot180X, $Rot270, $Rot270X ]; RotateSides: CoreOps.EachWirePairProc = { -- actualWire, publicWire oldSides: CoreBlock.Sides _ CoreBlock.GetWireSide[actualWire]; newSides: CoreBlock.Sides _ none; IF actualWire.size#0 THEN RETURN; FOR side: Side IN Side DO IF CoreBlock.OnSide[SideSides[side], oldSides] THEN newSides _ CoreBlock.AddSide[SideSides[OrientSideSide[orient][side]], newSides] ENDLOOP; CoreBlock.PutWireSide[publicWire, newSides]}; new _ PWC.RotateCellType[cell, OrientATOM[orient]]; []_CoreOps.VisitBinding[cell.public, new.public, RotateSides]; CoreBlock.PutCellSide[new, all]}; GetSize: PUBLIC PROC[frameCT: CT] = { variable: BOOL _ FALSE; dad, son: Size; frame: Frame _ FCT[frameCT]; horizontal: BOOL _ frame.first=left OR frame.first=right; glue: CoreGlue.Glue _ NARROW[frame.data]; IF frame.cell#NIL THEN { frame.size _ FixedSize[PWC.InterestSize[frame.cell]]; IF glue#NIL THEN IF glue.tDir=vertical THEN frame.size.xfree _ tame ELSE frame.size.yfree _ tame; RETURN}; IF frame.seq.size = 0 THEN { IF glue=NIL THEN {Signal[]; RETURN}; frame.size _ TameSize[]; IF glue.class=chan OR glue.class=pwr THEN IF glue.tDir=vertical THEN frame.size.xfree _ wild ELSE frame.size.yfree _ wild; IF frame.size.x < 1 OR frame.size.y < 1 THEN {Signal[]; frame.data _ NIL}; RETURN}; dad _ [0, 0, fixed, tame]; FOR i: INT IN [0..frame.seq.size) DO -- Normalize sons to x sequences for computations GetSize[frame.seq[i]]; son _ IF horizontal THEN FCT[frame.seq[i]].size ELSE FlipSize[FCT[frame.seq[i]].size]; CheckXSeqForExpansionError[dad, son, frame.seq[i]]; dad.xfree _ MAX[dad.xfree, son.xfree]; -- fixed, tame, wild dad.x _ dad.x + son.x; dad.yfree _ IF MIN[dad.yfree,son.yfree]=fixed THEN fixed ELSE MAX[dad.yfree,son.yfree]; dad.y _ MAX[dad.y, son.y]; ENDLOOP; FOR i: INT IN [0..frame.seq.size) DO son _ IF ~horizontal THEN FlipSize[FCT[frame.seq[i]].size] ELSE FCT[frame.seq[i]].size; IF dad.yfree=fixed AND son.yfree=wild THEN { log.PutF["\n WARNING: Limited wild %g width", IO.rope[ IF horizontal THEN "Y" ELSE "X" ]]; LogFCT[frameCT, 0, 2, log]; log.PutRope["\n"]; EXIT}; ENDLOOP; frame.size _ IF horizontal THEN dad ELSE FlipSize[dad]}; CheckXSeqForExpansionError: PROC[size1, size2: Size, frameCT: CT] = { IF (size1.y > size2.y AND size2.yfree=fixed) OR (size2.y > size1.y AND size1.yfree=fixed) THEN { log.PutF["\n ERROR: Size of %g not expandable", IO.rope[CoreName.CellNm[frameCT].n]]; LogFCT[FCT[frameCT].father, 0, 2, log]; log.Flush[]; Signal[]} }; ReSize: PUBLIC PROC[frameCT: CT] = { dad, son: Size; freedom: Freedom; oneVariable: BOOL; cnt: ARRAY Freedom OF INT _ ALL[0]; length, diff: INT _ 0; frame: Frame _ FCT[frameCT]; horizontal: BOOL _ frame.first=left OR frame.first=right; IF frame.seq.size = 0 THEN RETURN; dad _ IF horizontal THEN frame.size ELSE FlipSize[frame.size]; -- norm FOR ii: INT IN [0..frame.seq.size) DO son _ FCT[frame.seq[ii]].size; son _ IF horizontal THEN son ELSE FlipSize[son]; -- norm CheckXSeqForExpansionError[dad, son, frame.seq[ii]]; length _ length + son.x; cnt[son.xfree] _ cnt[son.xfree] + 1; SELECT dad.yfree FROM wild => IF son.yfree=fixed THEN Signal[]; -- how can this happen? tame => IF son.yfree#tame THEN Signal[]; -- how can this happen? ENDCASE; IF (dad.yfree >= son.yfree) AND (dad.y=son.y) THEN LOOP; log.PutF["\n %g width change %6g %6g", IO.rope [IF horizontal THEN "Y" ELSE "X"], IO.int[son.y], IO.rope[FreeRope[son.yfree]] ]; son.yfree _ MIN[dad.yfree, son.yfree]; son.y _ MAX[dad.y, son.y]; log.PutF[" to %6g %6g in: %g", IO.int[son.y], IO.rope[FreeRope[son.yfree]], IO.rope [CoreName.CellNm[frame.seq[ii]].n] ]; FCT[frame.seq[ii]].size _ IF horizontal THEN son ELSE FlipSize[son]; -- denorm ENDLOOP; diff _ dad.x - length; FOR freedom DECREASING IN Freedom DO IF cnt[freedom]>0 THEN EXIT ENDLOOP; oneVariable _ (cnt[tame]=1 AND cnt[wild]=0) OR (cnt[tame]=0 AND cnt[wild]=1); IF diff<0 THEN Signal[]; IF diff>0 OR diff=0 AND dad.xfree = fixed AND oneVariable THEN { SELECT dad.xfree FROM wild, tame => IF freedom#dad.xfree THEN Signal[]; ENDCASE; FOR ii: INT IN [0..frame.seq.size) WHILE cnt[freedom] > 0 DO incr: INT _ diff/cnt[freedom]; son _ FCT[frame.seq[ii]].size; son _ IF horizontal THEN son ELSE FlipSize[son]; -- norm; IF son.xfree#freedom THEN LOOP; log.PutF["\n %g length changed from %6g %6g", IO.rope[IF horizontal THEN "X" ELSE "Y"], IO.int[son.x], IO.rope[FreeRope[son.xfree]] ]; IF oneVariable THEN son.xfree _ dad.xfree; son.x _ son.x + incr; log.PutF[" to %6g %6g in: %g", IO.int[son.x], IO.rope[FreeRope[son.xfree]], IO.rope [CoreName.CellNm[frame.seq[ii]].n] ]; diff _ diff - incr; cnt[freedom] _ cnt[freedom] - 1; FCT[frame.seq[ii]].size _ IF horizontal THEN son ELSE FlipSize[son]; -- denorm ENDLOOP; IF diff#0 THEN ERROR}; FOR i: INT IN [0..frame.seq.size) DO ReSize[frame.seq[i]] ENDLOOP }; RePos: PUBLIC PROC[frameCT: CT, new: CD.Position] = { cng, pos: CD.Position; frame: Frame _ FCT[frameCT]; cng _ [new.x-frame.pos.x, new.y-frame.pos.y]; pos _ frame.pos _ new; IF frame.seq.size=0 THEN RETURN; FOR i: INT IN [0..frame.seq.size) DO RePos[frame.seq[i], pos]; SELECT frame.first FROM left, right => pos.x _ pos.x + FCT[frame.seq[i]].size.x; top, bottom => pos.y _ pos.y + FCT[frame.seq[i]].size.y; ENDCASE ENDLOOP }; FixOneSize: PUBLIC PROC [frameCT: CT] RETURNS[fixed1: BOOL _ FALSE] = { FixOneSizeRecursive: PUBLIC PROC [fframeCT: CT] RETURNS[ffixed1: BOOL _ FALSE] = { fframe: Frame _ FCT[fframeCT]; lastTame: INT _ -1; wildOrMultTamesFound: BOOL _ FALSE; horizontal: BOOL _ fframe.first=left OR fframe.first=right; FOR kid: INT IN [0..fframe.seq.size) DO SELECT (IF horizontal THEN FCT[fframe.seq[kid]].size.xfree ELSE FCT[fframe.seq[kid]].size.yfree) FROM fixed => LOOP; wild => wildOrMultTamesFound _ TRUE; ENDCASE => {IF lastTame#-1 THEN wildOrMultTamesFound _ TRUE; lastTame_kid}; ENDLOOP; IF lastTame#-1 AND wildOrMultTamesFound THEN { selectedFrame: Frame _ FCT[fframe.seq[lastTame]]; IF horizontal THEN {selectedFrame.size.xfree _ fixed; log.PutRope["\n X length"]} ELSE {selectedFrame.size.yfree _ fixed; log.PutRope["\n Y length"]}; log.PutF[" FIXED in %g", IO.rope[CoreName.CellNm[fframe.seq[lastTame]].n]]; ReSize[fframeCT]; RETURN[TRUE]}; FOR kid: INT IN [0..fframe.seq.size) WHILE NOT ffixed1 DO ffixed1 _ FixOneSizeRecursive[fframe.seq[kid]] ENDLOOP}; frame: Frame _ FCT[frameCT]; IF FixOneSizeRecursive[frameCT] THEN RETURN[TRUE]; IF frame.size.xfree=tame THEN {frame.size.xfree_fixed; ReSize[frameCT]; RETURN[TRUE]}; IF frame.size.yfree=tame THEN {frame.size.yfree_fixed; ReSize[frameCT]; RETURN[TRUE]}; RETURN[FALSE]}; AddExtentionIfNeeded: PROC[frameCT: Core.CellType] = { frame: Frame _ FCT[frameCT]; IF frame.cell#NIL AND frame.data#NIL THEN { -- Extendable glue glue: CoreGlue.Glue _ NARROW[frame.data]; size: CD.Position _ PWC.InterestSize[frame.cell]; extend: BOOL _ IF glue.tDir=vertical THEN frame.size.x#size.x ELSE frame.size.y#size.y; IF extend THEN { extFrame: Frame; AddExtention[frameCT]; extFrame _ FCT[FCT[frameCT].seq[0]]; extFrame.size.xfree _ extFrame.size.yfree _ fixed} } }; AddExtention: PUBLIC PROC[frameCT: CT] = { name: ROPE _ CoreName.CellNm[frameCT].n; origFrm: Frame _ FCT[frameCT]; newFrm: Frame; glue: CoreGlue.Glue _ NARROW[origFrm.data]; origConnSide: Side _ (IF glue.tDir=vertical THEN IF glue.type[left] = conn THEN left ELSE right ELSE IF glue.type[bottom] = conn THEN bottom ELSE top); origFrm.data _ NIL; origFrm.size.xfree _ origFrm.size.yfree _ fixed; newFrm _ NEW[CoreFrame.FrameRec _ [first: IF glue.tDir=vertical THEN left ELSE bottom]]; frameCT.data _ newFrm; newFrm.seq _ NEW[CoreFrame.FrameSeq[2]]; newFrm.seq[0] _ SELECT origConnSide FROM left => CoreGlue.CellProc[name: name.Cat["Ext"], l: ext, r: conn], right => CoreGlue.CellProc[name: name.Cat["Ext"], r: ext, l: conn], top => CoreGlue.CellProc[name: name.Cat["Ext"], t: ext, b: conn], bottom => CoreGlue.CellProc[name: name.Cat["Ext"], b: ext, t: conn], ENDCASE => ERROR; Expand[hard, newFrm.seq[0]]; newFrm.seq[1] _ CoreOps.SetCellTypeName[ NEW [Core.CellTypeRec _ [frameCT.class, NIL, origFrm]], name.Cat["Orig"]]; SELECT origConnSide FROM right, top => {new: CT _ newFrm.seq[0]; newFrm.seq[0] _ newFrm.seq[1]; newFrm.seq[1] _ new}; ENDCASE; newFrm.father _ origFrm.father; FCT[newFrm.seq[0]].father _ FCT[newFrm.seq[1]].father _ frameCT}; TellKidsAboutDad: PUBLIC PROC [frameCT: CT] = { dad: Frame _ FCT[frameCT]; FOR kid: INT IN [0..dad.seq.size) DO FCT[dad.seq[kid]].father _ frameCT; TellKidsAboutDad[dad.seq[kid]] ENDLOOP}; Neighbor: PUBLIC PROC [frameCT: CT, side: Side] RETURNS[neighborCT: CT, ok: BOOL] = { NeighborCheck: PROC[fCT: CT] RETURNS[type: {eq, ok, out}] = { frame: Frame _ FCT[fCT]; negh: CD.Rect _ CDBasics.RectAt[frame.pos, [frame.size.x,frame.size.y]]; SELECT neighborSide FROM right => { IF seg.x2=negh.x2 AND seg.y1=negh.y1 AND seg.y2=negh.y2 THEN RETURN[eq]; IF seg.x2 NOT IN (negh.x1 .. negh.x2] THEN RETURN[out]}; left => { IF seg.x1=negh.x1 AND seg.y1=negh.y1 AND seg.y2=negh.y2 THEN RETURN[eq]; IF seg.x1 NOT IN [negh.x1 .. negh.x2) THEN RETURN[out]}; top => { IF seg.y2=negh.y2 AND seg.x1=negh.x1 AND seg.x2=negh.x2 THEN RETURN[eq]; IF seg.y2 NOT IN (negh.y1 .. negh.y2] THEN RETURN[out]}; bottom => { IF seg.y1=negh.y1 AND seg.x1=negh.x1 AND seg.x2=negh.x2 THEN RETURN[eq]; IF seg.y1 NOT IN [negh.y1 .. negh.y2) THEN RETURN[out]}; ENDCASE; SELECT neighborSide FROM right, left => { IF seg.y1 NOT IN [negh.y1 .. negh.y2) THEN RETURN[out]; IF seg.y2 NOT IN (negh.y1 .. negh.y2] THEN RETURN[out]; RETURN[ok]}; top, bottom => { IF seg.x1 NOT IN [negh.x1 .. negh.x2) THEN RETURN[out]; IF seg.x2 NOT IN (negh.x1 .. negh.x2] THEN RETURN[out]; RETURN[ok]}; ENDCASE }; leaf: Frame _ FCT[frameCT]; leafNm: ROPE _ CoreName.CellNm[frameCT].n; neighborSide: Side _ OppSide[side]; seg: CD.Rect _ CDBasics.RectAt[leaf.pos, [leaf.size.x,leaf.size.y]]; SELECT side FROM top => seg.y1 _ seg.y2; bottom => seg.y2 _ seg.y1; right => seg.x1 _ seg.x2; left => seg.x2 _ seg.x1; ENDCASE; FOR neighborCT _ leaf.father, FCT[neighborCT].father WHILE neighborCT#NIL DO SELECT NeighborCheck[neighborCT] FROM ok => EXIT; out => LOOP; ENDCASE => ERROR REPEAT FINISHED => { log.PutF["\n ERROR: %g side neighbor of %g is external to frame", IO.rope[SideRope[side]], IO.rope[leafNm]]; RETURN[NIL, FALSE]} ENDLOOP; DO neighbor: Frame _ FCT[neighborCT]; IF neighbor.seq.size=0 THEN RETURN[neighborCT, FALSE]; FOR i: INT IN [0..neighbor.seq.size) DO SELECT NeighborCheck[neighbor.seq[i]] FROM out => LOOP; eq => {neighborCT _ neighbor.seq[i]; GOTO Found}; ENDCASE => {neighborCT _ neighbor.seq[i]; EXIT}; REPEAT Found=> EXIT; FINISHED => { log.PutF["\n ERROR: %g side neighbor of %g can't be identified", IO.rope[SideRope[side]], IO.rope[leafNm]]; RETURN[NIL, FALSE]} ENDLOOP; ENDLOOP; RETURN[neighborCT, TRUE]}; EnumFrame: PUBLIC PROC[fCT: CT, proc: EnumProc] RETURNS[kids, continue: BOOL _ TRUE] = { frame: Frame; [kids, continue] _ proc[fCT]; IF NOT continue THEN RETURN[FALSE, FALSE]; IF NOT kids THEN RETURN[FALSE, TRUE]; frame _ FCT[fCT]; FOR field: INT IN [0..frame.seq.size) DO IF NOT EnumFrame[frame.seq[field], proc].continue THEN RETURN[FALSE, FALSE]; ENDLOOP; RETURN[TRUE, TRUE]}; NextSide: PUBLIC PROC [side: Side] RETURNS [next: Side] = {RETURN[SELECT side FROM bottom=>right, right=>top, top=>left, left=>bottom, ENDCASE=>ERROR]}; PrevSide: PUBLIC PROC [side: Side] RETURNS [prev: Side] = {RETURN[SELECT side FROM bottom=>left, left=>top, top=>right, right=>bottom, ENDCASE=>ERROR]}; OppSide: PUBLIC PROC [side: Side] RETURNS [opp: Side] = {RETURN[SELECT side FROM left=>right, right=>left, top=>bottom, bottom=>top, ENDCASE=>ERROR]}; log: IO.STREAM _ NIL; GetLog: PUBLIC PROC RETURNS[IO.STREAM] = { logName: ROPE _ "CoreFrame.log"; IF log=NIL THEN { log _ ViewerIO.CreateViewerStreams[logName, NIL, logName].out; TypeScript.ChangeLooks[ViewerIO.GetViewerFromStream[log], 'f]; log.PutF["%g %g\n\n", IO.rope[logName], IO.time[]]}; RETURN[log]}; [ ] _ GetLog[]; END. 2CoreFrameImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited by Curry, October 4, 1986 7:47:32 pm PDT Don Curry October 30, 1986 5:37:16 pm PST checkPoint: ATOM _ CoreProperties.RegisterProperty[prop: $CheckPoint]; MarkFrameCheckPoint: PUBLIC PROC[frameCT: CT, name: ROPE _ NIL] = { name _ CoreName.CellNm[frameCT, name].n; CoreProperties.PutCellTypeProp[frameCT, checkPoint, NEW[BOOL _ TRUE]]}; IsFrameCheckPoint: PUBLIC PROC[frameCT: CT] RETURNS[BOOL] = { RETURN[CoreProperties.GetCellTypeProp[frameCT, checkPoint]#NIL]}; IF CoreName.CellNm[cell].n=NIL OR CoreName.CellNm[cell].n#CoreName.CellNm[frameCT].n THEN Signal[]; FCT[frameCT].cell.properties _ AddPropCopyIfNotPresent[FCT[frameCT].cell.properties, frameCT.properties]; SafeStorage.ReclaimCollectibleObjects[TRUE, TRUE]; AddPropCopyIfNotPresent: PROC [old, addends: Core.Properties] RETURNS [Core.Properties] = { consume: PROC [prop: ATOM, val: REF ANY _ NIL] = { IF CoreProperties.GetProp[old, prop]#NIL THEN RETURN; old _ CoreProperties.PutProp[old, prop, val]}; CoreProperties.Enumerate[CoreProperties.CopyProps[addends], consume]; RETURN[old]}; Brings up decorated record cellTypes and sets up Layout procs mainly to get rid of persistant identity cells RecastWithLayout: PUBLIC Core.RecastProc = { new _ me; IF new.class = CoreClasses.recordCellClass THEN RETURN; new _ PWC.FromLayoutWithoutPublic[ PWC.Layout[me] ]; IF new.class = CoreClasses.recordCellClass THEN RETURN; new _ PWC.FromLayout[ CoreOps.CopyWire[me.public], PWC.Layout[me] ]; IF new.class = CoreClasses.recordCellClass THEN RETURN; Signal[]}; RecastFrameSoft: PUBLIC Core.RecastProc = { frame: Frame _ FCT[me]; public: Core.Wires; intOnly: Core.Wires; instances: CoreCreate.CellInstances; ReOrderFrame[me]; new _ frame.cell; IF new#NIL THEN RETURN[new]; IF frame.seq.size=0 THEN { IF frame.intOnly=NIL THEN RETURN[frame.cell]; log.PutF["\n WARNING: %g has partially disconnected public.", IO.rope[CoreName.CellNm[frame.cell].n] ]; log.PutRope["\n I will bury the loose ends inside for now."]; instances _ LIST[ CoreCreate.Instance[frame.cell] ]} ELSE FOR i: INT DECREASING IN [0..frame.seq.size) DO new _ RecastFrameSoft[frame.seq[i]]; instances _ CONS[CoreCreate.Instance[new], instances] ENDLOOP; FOR ropes: LIST OF ROPE _ frame.public, ropes.rest WHILE ropes#NIL DO public _ CONS[CoreOps.CreateWires[0, ropes.first], public] ENDLOOP; FOR ropes: LIST OF ROPE _ frame.intOnly, ropes.rest WHILE ropes#NIL DO intOnly _ CONS[CoreOps.CreateWires[0, ropes.first], intOnly] ENDLOOP; new _ frame.cell _ CoreCreate.Cell[ public: CoreOps.CreateWire[public], onlyInternal: CoreOps.CreateWire[intOnly], instances: instances, name: CoreName.CellNm[me].n ]; CoreBlock.PutCellSide[new, SideSides[frame.first]]; CoreBlock.MergeSides[new]; SELECT frame.first FROM left => PWC.SetAbutX[new]; bottom => PWC.SetAbutY[new]; ENDCASE => Signal[] }; RecastFrameHard: PUBLIC Core.RecastProc = { frame: Frame _ FCT[me]; IF frame.cell#NIL THEN {Signal[]; RETURN[CoreOps.Recast[frame.cell]]} -- no no ELSE RETURN[FrameCell[me]]}; -- sets frame.cell RecastFrameHard: PUBLIC Core.RecastProc = { frame: Frame _ FCT[me]; name: ROPE _ CoreName.CellNm[me].n; cells: LIST OF Core.CellType; new _ frame.cell; ReOrderFrame[me]; IF new#NIL THEN RETURN[new]; IF frame.seq.size=0 THEN {Signal[]; RETURN[frame.cell]}; -- NIL FOR i: INT DECREASING IN [0..frame.seq.size) DO cells _ CONS[RecastFrameHard[frame.seq[i]], cells] ENDLOOP; new _ CoreBlock.AbutCellList[name: name, first: SideSides[frame.first], cellTypes: cells]; frame.cell _ new}; IF IsCheckPointFrame[frameCT] THEN IF ReadCheckPoint[frameCT] THEN RETURN; RotateCellType: PUBLIC PROC[cell: CT, orient: CD.Orientation] RETURNS[new: CT] = { MakeNew: PROC[oCell: CT, atom: ATOM, partial: CD.Orientation] RETURNS[nCell: CT]= { RotateSides: PROC[wire: Core.Wire] = { oldSides: CoreBlock.Sides _ CoreBlock.GetWireSide[wire]; newSides: CoreBlock.Sides _ none; FOR side: Side IN Side DO IF CoreBlock.OnSide[SideSides[side], oldSides] THEN newSides _ CoreBlock.AddSide[SideSides[OrientSideSide[partial][side]], newSides] ENDLOOP; CoreBlock.PutWireSide[wire, newSides]}; nCell _ PWC.RotateCellType[oCell, atom]; CoreOps.VisitAtomicWires[nCell.public, RotateSides]; CoreBlock.PutCellSide[nCell, all]}; SELECT orient FROM CDOrient.rotate90, CDOrient.rotate90X => new _ MakeNew[ cell, $Rot90, CDOrient.rotate90]; CDOrient.rotate180, CDOrient.rotate180X => new _ MakeNew[ cell, $Rot180, CDOrient.rotate180]; CDOrient.rotate270, CDOrient.rotate270X => new _ MakeNew[ cell, $Rot270, CDOrient.rotate270]; ENDCASE => new _ cell; SELECT orient FROM CDOrient.mirrorX, CDOrient.rotate90X, CDOrient.rotate180X, CDOrient.rotate270X => new _ MakeNew[ new, $FlipX, CDOrient.mirrorX]; ENDCASE}; Rotate: PUBLIC PROC[cell: CT, orient: CD.Orientation] RETURNS[new: CT] = { RotateSides: PROC[wire: Core.Wire] = { oldSides: CoreBlock.Sides _ CoreBlock.GetWireSide[wire]; newSides: CoreBlock.Sides _ none; FOR side: Side IN Side DO IF CoreBlock.OnSide[SideSides[side], oldSides] THEN newSides _ CoreBlock.AddSide[SideSides[OrientSideSide[orient][side]], newSides] ENDLOOP; CoreBlock.PutWireSide[wire, newSides]}; obj: CD.Object; new _ cell; WHILE new.class#CoreClasses.recordCellClass DO new _ CoreOps.Recast[new] ENDLOOP; SELECT orient FROM CDOrient.rotate90, CDOrient.rotate90X => {new _ PWC.RotateCellType[new, $Rot90]}; CDOrient.rotate180, CDOrient.rotate180X => {new _ PWC.RotateCellType[new, $Rot180]}; CDOrient.rotate270, CDOrient.rotate270X => {new _ PWC.RotateCellType[new, $Rot270]}; ENDCASE; SELECT orient FROM CDOrient.mirrorX, CDOrient.rotate90X, CDOrient.rotate180X, CDOrient.rotate270X => {new _ PWC.RotateCellType[new, $FlipX]}; ENDCASE; obj _ PWC.Layout[new]; IF PWC.FromLayoutWithoutPublic[obj]#new THEN Signal[]; CoreOps.VisitAtomicWires[new.public, RotateSides]; CoreBlock.PutCellSide[new, all]}; -- For this identity cellType IF incr#0 THEN AddExtentionIfNeeded[frame.seq[ii]]; FOR side: Side IN Side DO IF frame.shell.pins[side]=NIL THEN LOOP ELSE FOR pin: INT IN [0..frame.shell.pins[side].seq.size) DO frame.shell.pins[side][pin].pos.x _ frame.shell.pins[side][pin].pos.x + cng.x; frame.shell.pins[side][pin].pos.y _ frame.shell.pins[side][pin].pos.y + cng.y; ENDLOOP ENDLOOP; RETURN}; AddExtentionIfNeeded[fframe.seq[lastTame]]; -- could be too big!! FlattenOnce: PUBLIC PROC[frameCT: CT] RETURNS[new: Frame] = {new _ TestMergeNodes[frame]}; TestMergeNodes: PUBLIC PROC[frameCT: CT, testProc: TestMergeProc _ NIL] RETURNS[new: Frame] ~ { nlist: LIST OF REF; elist: LIST OF Frame; xory: XorY _ y; count: CARDINAL; FOR index: CARDINAL IN [0..frame.seq.size) DO IF testProc=NIL OR testProc[frame[index]] THEN { IF xory#frame[index].xory THEN ERROR; FOR element: CARDINAL IN [0..frame[index].seq.size) DO elist _ CONS[frame[index][element], elist] ENDLOOP} ELSE { IF elist#NIL THEN nlist _ CONS[elist, nlist]; elist _ NIL; nlist _ CONS[frame[index], nlist]}; REPEAT FINISHED => IF elist#NIL THEN nlist _ CONS[elist, nlist] ENDLOOP; count _ 0; FOR tlist: LIST OF REF _ nlist, tlist.rest WHILE tlist#NIL DO count _ count+1 ENDLOOP; new _ NewFrame[count, frame.xory, frame.name, frame.data, frame.unordered]; FOR node: CARDINAL DECREASING IN [0..new.seq.size) DO WITH nlist.first SELECT FROM list: LIST OF Frame => { cnt: CARDINAL _ 0; FOR tlist: LIST OF Frame _ list, tlist.rest WHILE tlist#NIL DO cnt _ cnt+1 ENDLOOP; new[node] _ NewFrame[cnt, xory, "Connections", , list.first.unordered]; FOR element: CARDINAL DECREASING IN [0..new[node].seq.size) DO new[node][element] _ list.first; list _ list.rest ENDLOOP}; plaFrame: Frame => {new[node] _ plaFrame}; ENDCASE => ERROR; nlist _ nlist.rest ENDLOOP}; Κ%θ˜šœ™Jšœ<™Jšœœ˜-—J˜šžœœœ œ˜-Jšœœ˜!Jšœœœ œ˜$Jšœœœœ˜Jšœœ˜ Jšœœ˜)Jšœœ ˜!Jšœ˜Jšœ/˜/Jšœ˜Jšœ"˜"Jšœœ˜/šœ™Jšœœ/™J—Jšœœ˜-Jšœžœ˜.Jšœœ˜0Jšœœ˜$Jšœ˜Jšœ-˜-Jšœ&œœ™2Jšœ˜Jšœ-˜-Jšœ6œ ˜AJšœM˜MJšœI˜IJšœI˜IJšœ>˜>Jšœœ˜-—J˜š žœœœœœœ˜BJšœ*˜*Jšœ ž œ ˜—šžœœ ™=Kšœ™š œ œœœœœ™2Kšœ#œœœ™5Kšœ.™.—KšœE™EKšœ™ —J˜š žœœœœœ˜TJš œ œœ œœ˜Ršœœ˜šœ)˜)Jšœ;˜;—šœ,˜,Jšœ<˜<—Jšœ˜——š žœœœœœœ˜\Jš œ œœ œœ˜Ršœœ˜šœœ˜&Jšœ9˜9—šœ!œ˜(Jšœ;˜;—Jšœ˜——Jšžœœ˜5J˜Jšœ=™=šžœœ˜+Jšœœ˜Jšœœ˜$Jšœœœœ˜Jšœ˜šœ œœ˜Jšœœ œΟc˜8š œœ œœ˜/Jšœœžœœ˜;—šœ#˜#Jšœ?˜?——Jšœ˜—J™Jšžœœ ˜8Jšœ.™.šžœœ™,Jšœ ™ Jšœ)œœ™7Jšœ4™4Jšœ)œœ™7JšœD™DJšœ)œœ™7Jšœ ™ —šžœœ™+Jšœœ™Jšœ™Jšœ™Jšœ$™$Jšœ™Jšœ™Jšœœœœ™šœ™šœ™Jšœœœœ ™-šœ=™=Jšœ'™)—Jšœ?™?Jšœ œ$™4—š œœœ œœ™4Jšœ$™$Jšœ œ&œ™>——š œœœœœœ™EJšœ œ.œ™C—š œœœœœœ™FJšœ œ/œ™E—šœ#™#Jšœ$™$Jšœ*™*Jšœ™Jšœ ™ —Jšœ3™3Jšœ™šœ ™Jšœ™Jšœ™Jšœ™——šžœœ™+Jšœœ™šœ ™Jšœ œ&™šœœœ˜(Jš œ œœœ œ ˜AJšœ&œ˜/——Jšœ˜ ——J˜šžœœœœœœ œœ˜Nšœ˜šœ$˜*Jšœœœœ˜(——Jšœ+˜+Jšœ$˜$—J˜Jšžœœœœœœœœœ˜SJšžœœœœœœœœœ˜QJ™š ž œœœ œ œœ˜Ošžœœ˜Jšœ œœ œ˜%šœœœ˜&šœ/˜/Jšœ8œ˜A———Jšœœ ˜Jšœ6˜6Jšœ!˜!šœ œœŸ)˜BJšœœœ˜(Jšœ0˜0Jšœ5˜5Jšœ˜—šœœŸ˜1Jšœœœ˜(Jšœ œœœ˜šœ œ˜šœ˜Jšœ˜ Jšœœœ7˜JJšœX˜XJšœ[˜[šœ œ˜šœ œœ˜%Jšœ2Ÿ˜G—Jšœ˜—šœ˜Jšœ˜Jšœ˜Jšœ˜šœ˜Jšœ œœ œ˜&šœ3˜3Jšœ'˜'—Jšœ$˜$Jšœœœ œ ˜9—Jšœ˜ ——Jšœœ˜—Jšœ˜—Jšœ2˜2JšœŸ˜0Jš œœœœ#œ˜P—J˜š žœ œœ œœœ ˜EJšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—J™š žœ œœ œœœ˜Rš ž œœœ œœŸ˜KJšœI˜I—šž œ9˜DJšœ>˜>Jšœ!˜!Jšœœœ˜!šœ œ˜šœ-˜3JšœO˜OJšœ˜——Jšœ-˜-—Jšœž œ ˜3Jšœ ž œ'˜>Jšœ!˜!—J˜J˜š žœ œœ œœœ™Rš žœœœ œœœ™Sšž œœ™&Jšœ8™8Jšœ!™!šœ œ™šœ-™3JšœP™PJšœ™——Jšœ'™'—Jšœ(™(Jšœ4™4Jšœ#™#—šœ™Jšœ™JšœΟbœ$™GJšœ™Jšœ œ%™IJšœ™Jšœ œ%™IJšœ™—šœ™Jšœ™Jšœ™Jšœ™Jšœžœ"™FJšœ™ ——J˜š žœ œœ œœœ™Jšž œœ™&Jšœ8™8Jšœ!™!šœ œ™šœ-™3JšœO™OJšœ™——Jšœ'™'—Jšœœ™Jšœ ™ Jšœ'œœ™Qšœ™Jšœ™Jšœ>™>Jšœ™Jšœ@™@Jšœ™JšœAœ™I—šœ™Jšœ™Jšœ™Jšœ™Jšœ@œ™H—Jšœ™Jšœ&œ ™6Jšœ2™2Jšœ"Ÿ™?J™—šžœœœ œ˜%Jšœ œœ˜Jšœ˜Jšœœ ˜Jšœ œœ˜9Jšœœ ˜)šœ œœ˜Jšœ5˜5šœœœœ˜&Jšœ˜Jšœ˜—Jšœ˜—šœœ˜Jšœœœ œ˜$Jšœ˜šœœœœ˜?Jšœ˜Jšœ˜—Jšœœœœ˜JJšœ˜—Jšœ˜š œœœœŸ1˜VJšœ˜šœœ ˜Jšœœ˜Jšœ œ˜&—Jšœ3˜3Jšœ œŸ˜;Jšœ˜Jš œ œœœœœ˜WJšœ œ˜Jšœ˜—šœœœ˜$šœœ ˜Jšœ œœœ˜B—šœœœ˜,šœ-˜-Jšœœ œœ˜,—Jšœ˜Jšœœ˜—Jšœ˜—Jšœ œ œœ˜8—J™šžœœœ˜Ešœœ˜/šœœœ˜0šœ/˜/Jšœ#˜%—Jšœœ˜'Jšœ ˜ Jšœ ˜ ———J™šžœœœ œ˜$Jšœ˜Jšœ˜Jšœ œ˜Jš œœ œœœ˜#Jšœœ˜Jšœœ ˜Jšœ œœ˜9Jšœœœ˜"Jšœœ œ œŸ˜Išœœœ˜%Jšœœ˜Jšœœ œœŸ˜>Jšœ4˜4Jšœ˜Jšœ$˜$šœ ˜Jšœ œœ Ÿ˜BJšœ œœ Ÿ˜AJšœ˜—Jšœœœœ˜8šœ'˜'Jšœœ œœ˜*Jšœ œ˜.—Jšœ œ˜&Jšœ œ˜šœ˜Jšœ œœ+˜Z—Jš œœ œœŸ ˜NJšœ˜—Jšœ˜Jšœ  œœ œœœœœ˜IJšœœœœ˜MJšœœ ˜š œœœœ œ˜@šœ ˜Jšœœœ ˜1Jšœ˜—š œœœœ˜Jšœœœ˜šœ-˜-Jšœœ œœ˜)Jšœ œ˜.—Jšœ œ˜*Jšœ˜šœ˜Jšœ œœ+˜Z—Jšœ˜Jšœ ˜ Jš œœ œœŸ ˜NJšœœ%™3Jšœ˜Jšœœœ˜——Jš œœœœœ˜D—J™š žœœœ œœ˜5Jšœ œ ˜Jšœœ ˜Jšœ-˜-Jšœ˜šœœœ˜ šœ™šœ™Jšœ ™ šœ<™™>——Jšœ*™*Jšœ™—Jšœ™——J™šž œœœœ˜/Jšœœœ˜(Jšœ ˜ Jšœ˜Jš œœ œœœœ˜+Jš œœœœœœ˜'Jšœœ˜šœœœ˜(Jš œœ,œœœœ˜LJšœ˜—Jšœœœ˜—J™šžœœœœ˜:šœœœœ4˜LJšœœ˜——šžœœœœ˜:šœœœœ4˜LJšœœ˜——šžœœœœ˜8šœœœœ4˜LJšœœ˜——J˜J˜Jšœœœœ˜š žœœœœœœ˜*Jšœ œ˜ šœœœ˜Jšœ,œ˜>Jšœ>˜>Jšœœœ ˜4—Jšœ˜ —J˜Jšœ˜J˜Jšœ˜—J˜—…—[BŸ\