DIRECTORY Basics, DragOps, DragOpsUtils, BitBlt; BitBltImpl: MONITOR IMPORTS Basics EXPORTS BitBlt = BEGIN BitAddress: TYPE = BitBlt.DragonBitAddress; DragonPointer: TYPE = BitBlt.DragonPointer; bitsPerWord: CARDINAL = Basics.bitsPerWord; BitBltInc: TYPE = RECORD[wVal, bitVal: INT, easy: BOOL]; -- easy if value MOD bitsPerWord = 0 srcAddr: BitAddress; -- address of source word at beginning of line dstAddr: BitAddress; -- address of destination word at beginning of line srcFunc: BitBlt.SrcFunc _ null; dstFunc: BitBlt.DstFunc _ null; srcInc, dstInc: BitBltInc; goingForward: BOOL _ TRUE; MaskArray: ARRAY [0..bitsPerWord] OF DragOps.Word; isThin: BOOL _ FALSE; -- true if dest is all within one word lMask: DragOps.Word; rMask: DragOps.Word; skew: DragOps.SixBitIndex; dWidth, sWidth: INT; -- width in words, including left & right partial words extraWordAtLeft, extraWordAtRight: BOOL; isGray: BOOL _ FALSE; grayParam: BitBlt.GrayParm; grayWidth: BitBltInc; grayBump: BitBltInc; lastGray: [0..15); InvalidBitBltCall: ERROR = CODE; BITBLT: PUBLIC PROC[bbt: BitBlt.BitBltTablePtr] = { grayHeightMod: INT; lineNum: INT _ DoSetup[bbt]; -- which line do we start with IF isGray THEN grayHeightMod _ grayParam.heightMinusOne+1; WHILE lineNum IN [0..bbt.height) DO sInc: BitBltInc; IF isGray THEN BltOneLineForwardGray[] ELSE IF goingForward THEN BltOneLineForward[] ELSE BltOneLineBackward[]; lineNum _ lineNum + (IF goingForward THEN 1 ELSE -1); sInc _ IF bbt.flags.gray THEN IF (lineNum MOD grayHeightMod) = lastGray THEN grayBump ELSE grayWidth ELSE srcInc; IF sInc.easy AND dstInc.easy THEN { srcAddr _ BitAddrInc[srcAddr, sInc]; dstAddr _ BitAddrInc[dstAddr, dstInc]; } ELSE Recompute[bbt, sInc]; ENDLOOP; }; DoSetup: PROC[bbt: BitBlt.BitBltTablePtr] RETURNS[lineNum: INT] = { goingForward _ bbt.flags.direction = forward; srcFunc _ bbt.flags.srcFunc; dstFunc _ bbt.flags.dstFunc; srcAddr _ bbt.src; -- true for either real source or gray IF (isGray _ bbt.flags.gray) THEN { grayParam _ LOOPHOLE[bbt.srcDesc]; IF grayParam.widthMinusOne # 0 OR grayParam.reserved # 0 OR ~goingForward OR ~bbt.flags.disjoint OR bbt.dstBpl < 0 THEN ERROR InvalidBitBltCall; grayWidth.easy _ TRUE; grayWidth.wVal _ grayParam.widthMinusOne+1; grayWidth.bitVal _ grayWidth.wVal * bitsPerWord; grayBump.easy _ TRUE; grayBump.wVal _ -grayWidth.wVal*grayParam.heightMinusOne; grayBump.bitVal _ grayBump.wVal * bitsPerWord; } ELSE { srcI: INT = LOOPHOLE[bbt.srcDesc]; IF srcI < 0 THEN srcInc.easy _ (-srcI MOD bitsPerWord) = 0 ELSE srcInc.easy _ (srcI MOD bitsPerWord) = 0; srcInc.wVal _ IF srcInc.easy THEN srcI/bitsPerWord ELSE -1; srcInc.bitVal _ srcI; IF (goingForward AND (srcInc.bitVal < 0 OR bbt.dstBpl < 0)) OR (~goingForward AND (srcInc.bitVal > 0 OR bbt.dstBpl > 0)) THEN ERROR InvalidBitBltCall; }; IF bbt.dstBpl < 0 THEN dstInc.easy _ (-bbt.dstBpl MOD bitsPerWord) = 0 ELSE dstInc.easy _ (bbt.dstBpl MOD bitsPerWord) = 0; dstInc.bitVal _ bbt.dstBpl; dstInc.wVal _ IF dstInc.easy THEN bbt.dstBpl/bitsPerWord ELSE -1; dstAddr _ bbt.dst; IF isGray THEN lastGray _ IF goingForward THEN grayParam.heightMinusOne - grayParam.yOffset ELSE grayParam.yOffset; lineNum _ IF goingForward THEN 0 ELSE bbt.height-1; BEGIN bitSkew: INT = srcAddr.bit - dstAddr.bit; finalDstInFirstLine: BitAddress _ BitAddrInc[bbt.dst, [wVal: -1, bitVal: bbt.width, easy: FALSE] ]; lMask _ MaskArray[dstAddr.bit]; rMask _ RMask[finalDstInFirstLine.bit]; skew _ IF bitSkew < 0 THEN bitsPerWord + bitSkew ELSE bitSkew; dWidth _ (dstAddr.bit + bbt.width + bitsPerWord - 1)/bitsPerWord; sWidth _ (srcAddr.bit + bbt.width + bitsPerWord - 1)/bitsPerWord; isThin _ (dWidth = 1); extraWordAtLeft _ extraWordAtRight _ FALSE; SELECT TRUE FROM sWidth > dWidth => extraWordAtLeft _ extraWordAtRight _ TRUE; sWidth < dWidth, sWidth = 1 => NULL; ENDCASE => IF bitSkew >= 0 THEN extraWordAtLeft _ TRUE ELSE extraWordAtRight _ TRUE; END; }; BitAddrInc: PROC[base: BitAddress, offset: BitBltInc] RETURNS[ba: BitAddress] = TRUSTED { IF offset.easy THEN { ba.word _ LOOPHOLE[offset.wVal + LOOPHOLE[base.word, INT], DragonPointer]; ba.bit _ base.bit; } ELSE { addedBits: INT = offset.bitVal + base.bit; absBits: INT = IF addedBits < 0 THEN -addedBits ELSE addedBits; wordPart: INT _ IntShiftRight[absBits, 5]; -- DragOps.logBitsPerWord is wrong IF addedBits < 0 THEN wordPart _ - wordPart; ba.word _ LOOPHOLE[LOOPHOLE[base.word, INT] + wordPart, DragonPointer]; ba.bit _ IntAnd[addedBits, bitsPerWord-1]; }; }; IntShiftRight: PROC[x: INT, dist: DragOps.SixBitIndex] RETURNS [INT] = TRUSTED INLINE { OPEN Basics; RETURN [LOOPHOLE[ Basics.ShiftRight[LOOPHOLE[x, LongNumber], dist], INT] ]; }; IntAnd: PROC[x, y: INT] RETURNS [INT] = TRUSTED INLINE { OPEN Basics; RETURN [ LOOPHOLE[ Basics.BITAND[LOOPHOLE[x], LOOPHOLE[y]], INT] ]; }; Recompute: PROC[bbt: BitBlt.BitBltTablePtr, sInc: BitBltInc] = { bitSkew: INT; finalDstAddrInLine: BitAddress; srcAddr _ BitAddrInc[srcAddr, [wVal: sInc.wVal, bitVal: sInc.bitVal, easy: sInc.easy] ]; dstAddr _ BitAddrInc[dstAddr, [wVal: dstInc.wVal, bitVal: dstInc.bitVal, easy: dstInc.easy] ]; bitSkew _ srcAddr.bit - dstAddr.bit; skew _ IF bitSkew < 0 THEN bitsPerWord + bitSkew ELSE bitSkew; finalDstAddrInLine _ BitAddrInc[dstAddr, [wVal: -1, bitVal: bbt.width, easy: FALSE] ]; lMask _ MaskArray[dstAddr.bit]; rMask _ RMask[finalDstAddrInLine.bit]; dWidth _ (dstAddr.bit + bbt.width + bitsPerWord - 1)/bitsPerWord; sWidth _ (srcAddr.bit + bbt.width + bitsPerWord - 1)/bitsPerWord; isThin _ dWidth = 1; extraWordAtLeft _ extraWordAtRight _ FALSE; SELECT TRUE FROM sWidth > dWidth => extraWordAtLeft _ extraWordAtRight _ TRUE; sWidth < dWidth, sWidth = 1 => NULL; ENDCASE => IF bitSkew >= 0 THEN extraWordAtLeft _ TRUE ELSE extraWordAtRight _ TRUE; }; BltOneLineForward: PROC = TRUSTED { src: DragonPointer _ srcAddr.word; dst: DragonPointer _ dstAddr.word; longSrcHi, longSrcLo: DragOps.Word; dstValue, srcWord, curVal: DragOps.Word; width: INT _ dWidth; longSrcHi _ longSrcLo _ FetchWord[src]; src _ LOOPHOLE[src+1]; IF extraWordAtLeft THEN { longSrcLo _ FetchWord[src]; src _ LOOPHOLE[src+1]; }; [srcWord, dstValue] _ ComputeDstValue[longSrcHi, longSrcLo, dst]; IF isThin THEN { doubleMask: DragOps.Word = Basics.BITAND[lMask, rMask]; curVal _ Basics.BITAND[FetchWord[dst], Basics.BITNOT[doubleMask]]; dstValue _ Basics.BITAND[dstValue, doubleMask]; StoreWord[dst, Basics.BITOR[curVal, dstValue] ]; RETURN; }; dstValue _ Basics.BITAND[dstValue, lMask]; -- at left side, so do left mask curVal _ Basics.BITAND[FetchWord[dst], Basics.BITNOT[lMask]]; dstValue _ Basics.BITOR[curVal, dstValue]; DO -- store full words in this loop StoreWord[dst, dstValue]; dst _ LOOPHOLE[dst + 1]; longSrcHi _ longSrcLo; -- (lshift bitsPerWord) IF width <=2 THEN EXIT; longSrcLo _ FetchWord[src]; [srcWord, dstValue] _ ComputeDstValue[longSrcHi, longSrcLo, dst]; src _ LOOPHOLE[src+1]; width _ width - 1; ENDLOOP; IF extraWordAtRight THEN longSrcLo _ FetchWord[src]; [srcWord, dstValue] _ ComputeDstValue[longSrcHi, longSrcLo, dst]; curVal _ Basics.BITAND[FetchWord[dst], Basics.BITNOT[rMask] ]; dstValue _ Basics.BITAND[dstValue, rMask]; -- at right side, so do right mask StoreWord[dst, Basics.BITOR[curVal, dstValue] ]; }; BltOneLineForwardGray: PROC = TRUSTED { src: DragonPointer _ srcAddr.word; dst: DragonPointer _ dstAddr.word; longSrcHi, longSrcLo: DragOps.Word; dstValue, srcWord, curVal: DragOps.Word; width: INT _ dWidth; count: INT _ 0; longSrcHi _ longSrcLo _ FetchWord[src]; src _ GrayInc[src, count _ count + 1]; IF extraWordAtLeft THEN { longSrcLo _ FetchWord[src]; src _ GrayInc[src, count _ count + 1]; }; [srcWord, dstValue] _ ComputeDstValue[longSrcHi, longSrcLo, dst]; IF isThin THEN { doubleMask: DragOps.Word = Basics.BITAND[lMask, rMask]; curVal _ Basics.BITAND[FetchWord[dst], Basics.BITNOT[doubleMask]]; dstValue _ Basics.BITAND[dstValue, doubleMask]; StoreWord[dst, Basics.BITOR[curVal, dstValue]]; RETURN; }; dstValue _ Basics.BITAND[dstValue, lMask]; -- at left side, do left mask curVal _ Basics.BITAND[FetchWord[dst], Basics.BITNOT[lMask]]; dstValue _ Basics.BITOR[curVal, dstValue]; DO -- store full words in this loop StoreWord[dst, dstValue]; dst _ LOOPHOLE[dst + 1]; longSrcHi _ longSrcLo; IF width <=2 THEN EXIT; longSrcLo _ FetchWord[src]; [srcWord, dstValue] _ ComputeDstValue[longSrcHi, longSrcLo, dst]; src _ GrayInc[src, count _ count + 1]; width _ width - 1; ENDLOOP; IF extraWordAtRight THEN longSrcLo _ FetchWord[src]; [srcWord, dstValue] _ ComputeDstValue[longSrcHi, longSrcLo, dst]; curVal _ Basics.BITAND[FetchWord[dst], Basics.BITNOT[rMask] ]; dstValue _ Basics.BITAND[dstValue, rMask]; -- at right side, so do right mask StoreWord[dst, Basics.BITOR[curVal, dstValue]]; }; BltOneLineBackward: PROC = TRUSTED { src: DragonPointer _ LOOPHOLE[srcAddr.word + sWidth - 1]; dst: DragonPointer _ LOOPHOLE[dstAddr.word + dWidth - 1]; longSrcHi, longSrcLo: DragOps.Word; dstValue, srcWord, curVal: DragOps.Word; width: INTEGER _ dWidth; longSrcHi _ longSrcLo _ FetchWord[src]; src _ LOOPHOLE[src-1]; IF extraWordAtRight THEN { longSrcHi _ FetchWord[src]; src _ LOOPHOLE[src-1]; }; [srcWord, dstValue] _ ComputeDstValue[longSrcHi, longSrcLo, dst]; IF isThin THEN { doubleMask: DragOps.Word = Basics.BITAND[lMask, rMask]; curVal _ Basics.BITAND[FetchWord[dst], Basics.BITNOT[doubleMask]]; dstValue _ Basics.BITAND[dstValue, doubleMask]; StoreWord[dst, Basics.BITOR[curVal, dstValue]]; RETURN; }; dstValue _ Basics.BITAND[dstValue, rMask]; -- at right side, do right mask curVal _ Basics.BITAND[FetchWord[dst], Basics.BITNOT[rMask] ]; dstValue _ Basics.BITOR[curVal, dstValue]; DO -- store full words in this loop StoreWord[dst, dstValue]; dst _ LOOPHOLE[dst-1]; longSrcLo _ longSrcHi; -- cheaper than shifting IF width <=2 THEN EXIT; longSrcHi _ FetchWord[src]; src _ LOOPHOLE[src-1]; [srcWord, dstValue] _ ComputeDstValue[longSrcHi, longSrcLo, dst]; width _ width - 1; ENDLOOP; IF extraWordAtLeft THEN longSrcHi _ FetchWord[src]; [srcWord, dstValue] _ ComputeDstValue[longSrcHi, longSrcLo, dst]; dstValue _ Basics.BITAND[dstValue, lMask]; -- at left side, so do left mask curVal _ Basics.BITAND[FetchWord[dst], Basics.BITNOT[lMask]]; StoreWord[dst, Basics.BITOR[curVal, dstValue]]; }; ComputeDstValue: PROC[longSrcHi, longSrcLo: DragOps.Word, dst: DragonPointer] RETURNS[srcWord, dstValue: DragOps.Word] = { srcWord _ LOOPHOLE[Basics.DoubleWordShiftLeft[LOOPHOLE[longSrcHi], LOOPHOLE[longSrcLo], skew]]; IF srcFunc = complement THEN srcWord _ Basics.BITNOT[srcWord]; dstValue _ SELECT dstFunc FROM null => srcWord, and => Basics.BITAND[srcWord, FetchWord[dst]], or => Basics.BITOR[srcWord, FetchWord[dst]], xor => Basics.BITXOR[srcWord, FetchWord[dst]], ENDCASE => ERROR InvalidBitBltCall; }; FetchWord: PROC[ptr: DragonPointer] RETURNS[DragOps.Word] = TRUSTED { x: DragonPointer = LOOPHOLE[2*LOOPHOLE[ptr, INT]]; RETURN[x^]; }; StoreWord: PROC[ptr: DragonPointer, value: DragOps.Word] = TRUSTED { x: DragonPointer = LOOPHOLE[2*LOOPHOLE[ptr, INT]]; x^ _ value; }; GrayInc: PROC[src: DragonPointer, count: INT] RETURNS[DragonPointer] = { inc: INT; IF grayWidth.wVal = 1 THEN RETURN[src] -- special case ELSE inc _ count MOD grayWidth.wVal; RETURN[LOOPHOLE[LOOPHOLE[src, INT]+inc]]; }; RMask: PROC[bitNum: [0..bitsPerWord)] RETURNS[DragOps.Word] = { SELECT bitNum FROM 0 => RETURN[DragOps.OnesWord]; -- really bit 32 ENDCASE => RETURN[Basics.BITNOT[MaskArray[bitNum]] ]; }; MaskArray[ 0] _ DragOps.OnesWord; MaskArray[ 1] _ LOOPHOLE[17777777777B]; MaskArray[ 2] _ LOOPHOLE[ 7777777777B]; MaskArray[ 3] _ LOOPHOLE[ 3777777777B]; MaskArray[ 4] _ LOOPHOLE[ 1777777777B]; MaskArray[ 5] _ LOOPHOLE[ 777777777B]; MaskArray[ 6] _ LOOPHOLE[ 377777777B]; MaskArray[ 7] _ LOOPHOLE[ 177777777B]; MaskArray[ 8] _ LOOPHOLE[ 77777777B]; MaskArray[ 9] _ LOOPHOLE[ 37777777B]; MaskArray[10] _ LOOPHOLE[ 17777777B]; MaskArray[11] _ LOOPHOLE[ 7777777B]; MaskArray[12] _ LOOPHOLE[ 3777777B]; MaskArray[13] _ LOOPHOLE[ 1777777B]; MaskArray[14] _ LOOPHOLE[ 777777B]; MaskArray[15] _ LOOPHOLE[ 377777B]; MaskArray[16] _ LOOPHOLE[ 177777B]; MaskArray[17] _ LOOPHOLE[ 77777B]; MaskArray[18] _ LOOPHOLE[ 37777B]; MaskArray[19] _ LOOPHOLE[ 17777B]; MaskArray[20] _ LOOPHOLE[ 7777B]; MaskArray[21] _ LOOPHOLE[ 3777B]; MaskArray[22] _ LOOPHOLE[ 1777B]; MaskArray[23] _ LOOPHOLE[ 777B]; MaskArray[24] _ LOOPHOLE[ 377B]; MaskArray[25] _ LOOPHOLE[ 177B]; MaskArray[26] _ LOOPHOLE[ 77B]; MaskArray[27] _ LOOPHOLE[ 37B]; MaskArray[28] _ LOOPHOLE[ 17B]; MaskArray[29] _ LOOPHOLE[ 7B]; MaskArray[30] _ LOOPHOLE[ 3B]; MaskArray[31] _ LOOPHOLE[ 1B]; END. ^BitBltImpl.mesa Copyright (C) 1986 by Xerox Corporation. All rights reserved. Willie-Sue, September 12, 1986 2:50:40 pm PDT Carl Hauser, April 30, 1987 1:20:52 pm PDT dWidth & sWidth differ by ar most +/-1 dWidth > 2 => left partial, dWidth-2 full words, and right partial dWidth = 2 => left partial, right partial only dWidth = 1 => entire destination within one word - special case as both masks need to be applied at the same time If sWidth = dWidth+1, an extra source word must be fetched at both ends. If sWidth = dWidth-1 or sWidth = 1, no extra source words need be fetched. If sWidth = dWidth # 1, an extra source word must be fetched at one end: if src.bit mod bitsPerWord > dst.bit mod bitsPerWord then left else right BITBLT: PUBLIC ENTRY PROC[bbt: DragonBitBlt.BitBltTablePtr] = { ENABLE UNWIND, ABORTED => NULL; check for legality IF bbt.flags.reserved # 0 OR bbt.reserved # 0 OR bbt.src.reserved # 0 OR bbt.dst.reserved # 0 OR (~bbt.flags.gray AND LOOPHOLE[bbt.srcDesc, INTEGER] = 0) OR -- bbt.dstBpl = 0 OR bbt.width > LAST[NAT] OR bbt.height > LAST[NAT] THEN ERROR InvalidBitBltCall; more checking checking for thin case, setting up masks, widths for line wordPart: INT _ IntShiftRight[absBits, DragOps.logBitsPerWord]; either srcBpl or dstBpl was not a multiple of bitsPerWord; recompute masks for this line dst^ _ Basics.BITOR[curVal, dstValue]; Κΐ˜™J™>Icode™-K™*—J˜šΟk ˜ J˜J˜J˜ J˜—J˜šΟn œ˜š˜Jšœ˜—J˜Jšœ˜J˜Jšœœ˜—J˜Jšœ œ˜+Jšœœ˜+Jšœ œ˜+Jš œ œœœœΟc$˜]J˜JšœŸ.˜DJšœŸ3˜IJšœ˜Jšœ˜Jšœ˜Jšœœœ˜Jšž œœœ˜2J˜JšœœœŸ&˜>Jšœ˜Jšœ˜Jšœ˜J˜šœœŸ7˜MJšœ&™&JšœB™BJšœ.™.Jšœq™q—šœ#œ˜(JšœH™HJ™J™HJ™I——J˜Jšœœœ˜Jšœ˜Jšœ˜Jšœ˜J˜J˜Jšžœœœ˜!J˜Jšœœœœ&™?šœœœ ˜3Jšœœœœ™Jšœœ˜Jšœ œŸ˜šœœœ˜>Jšœœ˜——J˜—šœœœ˜FJšœœ˜4—Jšœ˜Jšœœ œœ˜AJ˜šœ˜J˜——šœœ œ˜.J˜,Jšœ˜—Jšœ œœœ˜3J™Jšœ9™9š˜Jšœ œ˜)šœ!˜!Jšœ8œ˜A—Jšœ˜Jšœ'˜'Jšœœ œœ ˜>JšœA˜AJšœA˜AJšœ˜Jšœ%œ˜+šœœ˜Jšœ8œ˜=Jšœœ˜$šœ˜ šœœ˜+Jšœœ˜———Jšœ˜—J˜—J˜šž œœ&œœ˜Yšœ œ˜Jšœ œœ œ˜JJšœ˜J˜šœ˜Jšœ œ˜*Jš œ œœœ œ ˜?Jšœ œ2™?Jšœ œ@˜MJšœœ˜,Jšœ œœ œ˜GJšœ*˜*J˜——J˜—J˜šž œœœœœœœ˜WJšœ˜ Jšœœœœ˜KJ˜—J˜šžœœœœœœœ˜8Jšœ˜ šœ˜Jš œ œœœœ˜:—J˜—J˜šž œœ1˜@JšœX™XJšœ œ˜ Jšœ˜J˜JšœX˜XJšœ^˜^J˜Jšœ$˜$Jšœœ œœ ˜>JšœMœ˜VJšœ˜Jšœ&˜&JšœA˜AJšœA˜AJšœ˜Jšœ%œ˜+šœœ˜Jšœ8œ˜=Jšœœ˜$šœ˜ šœœ˜+Jšœœ˜———J˜—J˜šžœœœ˜#Jšœ"˜"Jšœ"˜"Jšœ#˜#Jšœ(˜(Jšœœ ˜J˜Jšœ'˜'Jšœœ˜šœœ˜Jšœ˜Jšœœ˜J˜—JšœA˜AJ˜šœœ˜Jšœ"œ˜7Jšœœœ˜BJšœœ˜/Jšœœ™&Jšœœ˜0Jšœ˜J˜—J˜JšœœŸ ˜LJšœœœ ˜=Jšœœ˜*J˜šœŸ ˜$Jšœ˜Jšœœ ˜J˜JšœŸ˜/Jšœ œœ˜Jšœ˜JšœA˜AJšœœ˜Jšœ˜Jšœ˜—J˜Jšœœ˜4JšœA˜AJšœœœ ˜>JšœœŸ"˜NJšœœ˜0Jšœ˜—J˜šžœœœ˜'Jšœ"˜"Jšœ"˜"Jšœ#˜#Jšœ(˜(Jšœœ ˜Jšœœ˜J˜Jšœ'˜'Jšœ&˜&šœœ˜Jšœ˜Jšœ&˜&J˜—JšœA˜AJ˜šœœ˜Jšœ"œ˜7Jšœœœ˜BJšœœ˜/Jšœœ˜/Jšœ˜J˜—J˜JšœœŸ˜IJšœœœ ˜=Jšœœ˜*J˜šœŸ ˜$Jšœ˜Jšœœ ˜J˜Jšœ˜Jšœ œœ˜Jšœ˜JšœA˜AJšœ&˜&Jšœ˜Jšœ˜—J˜Jšœœ˜4JšœA˜AJ˜Jšœœœ ˜>JšœœŸ"˜NJšœœ˜/Jšœ˜—J˜šžœœœ˜$Jšœœ˜9Jšœœ˜9Jšœ#˜#Jšœ(˜(Jšœœ ˜J˜Jšœ'˜'šœœ˜Jšœœ˜Jšœ˜Jšœœ˜J˜—JšœA˜AJ˜šœœ˜Jšœ"œ˜7Jšœœœ˜BJšœœ˜/Jšœœ˜/Jšœ˜J˜—J˜JšœœŸ˜KJšœœœ ˜>Jšœœ˜*J˜šœŸ ˜$Jšœ˜Jšœœ˜J˜JšœŸ˜0Jšœ œœ˜Jšœ˜Jšœœ˜JšœA˜AJšœ˜Jšœ˜—J™Jšœœ˜4JšœA˜AJ˜JšœœŸ ˜LJšœœœ ˜=Jšœœ˜/Jšœ˜J˜—šžœœ8˜MJšœœ%˜-Kšœ œœ œ˜`Jšœœœ ˜>šœ œ ˜Jšœ˜Jšœœ˜.Jšœ œ˜,Jšœœ˜.Jšœœ˜#—J˜—J˜šž œœœœ˜EJšœœœœ˜2Jšœ˜ J˜—J˜šž œœ,œ˜DJšœœœœ˜2Jšœ ˜ J˜—J˜šžœœœœ˜HJšœœ˜ šœœœŸ˜7Jšœ œ˜$—Jšœœœœ˜)J˜—J˜šžœœœ˜?šœ˜JšœœŸ˜1Jšœœœ˜5—J˜—J˜Jšœ!˜!Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'Jšœœ˜'J˜J˜šœ˜J˜——…—1ΊFΨ