DIRECTORY Basics, DragOpsCross, DragOpsCrossUtils, DragonBitBlt; DragonBitBltImpl: MONITOR IMPORTS Basics, DragOpsCrossUtils EXPORTS DragonBitBlt = BEGIN OPEN DragOpsCrossUtils; BitAddress: TYPE = DragonBitBlt.DragonBitAddress; DragonPointer: TYPE = DragonBitBlt.DragonPointer; bitsPerWord: CARDINAL = DragOpsCross.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: DragonBitBlt.SrcFunc _ null; dstFunc: DragonBitBlt.DstFunc _ null; srcInc, dstInc: BitBltInc; goingForward: BOOL _ TRUE; MaskArray: ARRAY [0..bitsPerWord] OF DragOpsCross.Word; isThin: BOOL _ FALSE; -- true if dest is all within one word lMask: DragOpsCross.Word; rMask: DragOpsCross.Word; skew: DragOpsCross.SixBitIndex; dWidth, sWidth: INT; -- width in words, including left & right partial words extraWordAtLeft, extraWordAtRight: BOOL; isGray: BOOL _ FALSE; grayParam: DragonBitBlt.GrayParm; grayWidth: BitBltInc; grayBump: BitBltInc; lastGray: [0..15); InvalidBitBltCall: ERROR = CODE; BITBLT: PUBLIC PROC[bbt: DragonBitBlt.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: DragonBitBlt.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]; -- DragOpsCross.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: DragOpsCross.SixBitIndex] RETURNS [INT] = TRUSTED INLINE { OPEN Basics; RETURN [LOOPHOLE[ DoubleShiftRight[LOOPHOLE[x, LongNumber], dist], INT] ]; }; IntAnd: PROC[x, y: INT] RETURNS [INT] = TRUSTED INLINE { OPEN Basics; RETURN [ LOOPHOLE[ DoubleAnd[LOOPHOLE[x, LongNumber], LOOPHOLE[y, LongNumber]], INT] ]; }; Recompute: PROC[bbt: DragonBitBlt.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: DragOpsCross.Word; dstValue, srcWord, curVal: DragOpsCross.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: DragOpsCross.Word = DragAnd[lMask, rMask]; curVal _ DragAnd[FetchWord[dst], DragNot[doubleMask]]; dstValue _ DragAnd[dstValue, doubleMask]; StoreWord[dst, DragOr[curVal, dstValue] ]; RETURN; }; dstValue _ DragAnd[dstValue, lMask]; -- at left side, so do left mask curVal _ DragAnd[FetchWord[dst], DragNot[lMask]]; dstValue _ DragOr[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 _ DragAnd[FetchWord[dst], DragNot[rMask] ]; dstValue _ DragAnd[dstValue, rMask]; -- at right side, so do right mask StoreWord[dst, DragOr[curVal, dstValue] ]; }; BltOneLineForwardGray: PROC = TRUSTED { src: DragonPointer _ srcAddr.word; dst: DragonPointer _ dstAddr.word; longSrcHi, longSrcLo: DragOpsCross.Word; dstValue, srcWord, curVal: DragOpsCross.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: DragOpsCross.Word = DragAnd[lMask, rMask]; curVal _ DragAnd[FetchWord[dst], DragNot[doubleMask]]; dstValue _ DragAnd[dstValue, doubleMask]; StoreWord[dst, DragOr[curVal, dstValue]]; RETURN; }; dstValue _ DragAnd[dstValue, lMask]; -- at left side, do left mask curVal _ DragAnd[FetchWord[dst], DragNot[lMask]]; dstValue _ DragOr[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 _ DragAnd[FetchWord[dst], DragNot[rMask] ]; dstValue _ DragAnd[dstValue, rMask]; -- at right side, so do right mask StoreWord[dst, DragOr[curVal, dstValue]]; }; BltOneLineBackward: PROC = TRUSTED { src: DragonPointer _ LOOPHOLE[srcAddr.word + sWidth - 1]; dst: DragonPointer _ LOOPHOLE[dstAddr.word + dWidth - 1]; longSrcHi, longSrcLo: DragOpsCross.Word; dstValue, srcWord, curVal: DragOpsCross.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: DragOpsCross.Word = DragAnd[lMask, rMask]; curVal _ DragAnd[FetchWord[dst], DragNot[doubleMask]]; dstValue _ DragAnd[dstValue, doubleMask]; StoreWord[dst, DragOr[curVal, dstValue]]; RETURN; }; dstValue _ DragAnd[dstValue, rMask]; -- at right side, do right mask curVal _ DragAnd[FetchWord[dst], DragNot[rMask] ]; dstValue _ DragOr[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 _ DragAnd[dstValue, lMask]; -- at left side, so do left mask curVal _ DragAnd[FetchWord[dst], DragNot[lMask]]; StoreWord[dst, DragOr[curVal, dstValue]]; }; ComputeDstValue: PROC[longSrcHi, longSrcLo: DragOpsCross.Word, dst: DragonPointer] RETURNS[srcWord, dstValue: DragOpsCross.Word] = { srcWord _ DragOpsCrossUtils.DoubleWordShiftLeft[longSrcHi, longSrcLo, skew]; IF srcFunc = complement THEN srcWord _ DragNot[srcWord]; dstValue _ SELECT dstFunc FROM null => srcWord, and => DragAnd[srcWord, FetchWord[dst]], or => DragOr[srcWord, FetchWord[dst]], xor => DragXor[srcWord, FetchWord[dst]], ENDCASE => ERROR InvalidBitBltCall; }; FetchWord: PROC[ptr: DragonPointer] RETURNS[DragOpsCross.Word] = TRUSTED { x: DragonPointer = LOOPHOLE[2*LOOPHOLE[ptr, INT]]; RETURN[x^]; }; StoreWord: PROC[ptr: DragonPointer, value: DragOpsCross.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[DragOpsCross.Word] = { SELECT bitNum FROM 0 => RETURN[DragOpsCross.OnesWord]; -- really bit 32 ENDCASE => RETURN[DragNot[MaskArray[bitNum]] ]; }; MaskArray[ 0] _ DragOpsCross.OnesWord; MaskArray[ 1] _ DragOpsCrossUtils.IntToWord[17777777777B]; MaskArray[ 2] _ DragOpsCrossUtils.IntToWord[ 7777777777B]; MaskArray[ 3] _ DragOpsCrossUtils.IntToWord[ 3777777777B]; MaskArray[ 4] _ DragOpsCrossUtils.IntToWord[ 1777777777B]; MaskArray[ 5] _ DragOpsCrossUtils.IntToWord[ 777777777B]; MaskArray[ 6] _ DragOpsCrossUtils.IntToWord[ 377777777B]; MaskArray[ 7] _ DragOpsCrossUtils.IntToWord[ 177777777B]; MaskArray[ 8] _ DragOpsCrossUtils.IntToWord[ 77777777B]; MaskArray[ 9] _ DragOpsCrossUtils.IntToWord[ 37777777B]; MaskArray[10] _ DragOpsCrossUtils.IntToWord[ 17777777B]; MaskArray[11] _ DragOpsCrossUtils.IntToWord[ 7777777B]; MaskArray[12] _ DragOpsCrossUtils.IntToWord[ 3777777B]; MaskArray[13] _ DragOpsCrossUtils.IntToWord[ 1777777B]; MaskArray[14] _ DragOpsCrossUtils.IntToWord[ 777777B]; MaskArray[15] _ DragOpsCrossUtils.IntToWord[ 377777B]; MaskArray[16] _ DragOpsCrossUtils.IntToWord[LONG[177777B]]; MaskArray[17] _ DragOpsCrossUtils.IntToWord[LONG[ 77777B]]; MaskArray[18] _ DragOpsCrossUtils.IntToWord[LONG[ 37777B]]; MaskArray[19] _ DragOpsCrossUtils.IntToWord[LONG[ 17777B]]; MaskArray[20] _ DragOpsCrossUtils.IntToWord[LONG[ 7777B]]; MaskArray[21] _ DragOpsCrossUtils.IntToWord[LONG[ 3777B]]; MaskArray[22] _ DragOpsCrossUtils.IntToWord[LONG[ 1777B]]; MaskArray[23] _ DragOpsCrossUtils.IntToWord[LONG[ 777B]]; MaskArray[24] _ DragOpsCrossUtils.IntToWord[LONG[ 377B]]; MaskArray[25] _ DragOpsCrossUtils.IntToWord[LONG[ 177B]]; MaskArray[26] _ DragOpsCrossUtils.IntToWord[LONG[ 77B]]; MaskArray[27] _ DragOpsCrossUtils.IntToWord[LONG[ 37B]]; MaskArray[28] _ DragOpsCrossUtils.IntToWord[LONG[ 17B]]; MaskArray[29] _ DragOpsCrossUtils.IntToWord[LONG[ 7B]]; MaskArray[30] _ DragOpsCrossUtils.IntToWord[LONG[ 3B]]; MaskArray[31] _ DragOpsCrossUtils.IntToWord[LONG[ 1B]]; END. 8DragonBitBltImpl.mesa Copyright (C) 1986 by Xerox Corporation. All rights reserved. Willie-Sue, September 12, 1986 2:50:40 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, DragOpsCross.logBitsPerWord]; either srcBpl or dstBpl was not a multiple of bitsPerWord; recompute masks for this line dst^ _ DragOr[curVal, dstValue]; Κ˜™J™>Icode™-—J˜šΟk ˜ J˜J˜ J˜J˜ —J˜šΟnœ˜š˜Jšœ˜—J˜Jšœ ˜J˜Jšœœœ˜—J˜Jšœ œ!˜1Jšœœ˜1Jšœ œ˜1Jš œ œœœœΟc$˜]J˜JšœŸ.˜DJšœŸ3˜IJšœ%˜%Jšœ%˜%Jšœ˜Jšœœœ˜Jšž œœœ˜7J˜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šœœœœ&™?šœœœ&˜9Jšœœœœ™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šœ œ7™DJšœ œE˜RJšœœ˜,Jšœ œœ œ˜GJšœ*˜*J˜——J˜—J˜šž œœœ"œœœœ˜\Jšœ˜ Jšœœœœ˜JJ˜—J˜šžœœœœœœœ˜8Jšœ˜ šœ˜Jšœ œœœ˜N—J˜—J˜šž œœ7˜FJšœ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šœ6˜6Jšœ6˜6Jšœ)˜)Jšœ ™ Jšœ*˜*Jšœ˜J˜—J˜Jšœ&Ÿ ˜FJšœ1˜1Jšœ$˜$J˜šœŸ ˜$Jšœ˜Jšœœ ˜J˜JšœŸ˜/Jšœ œœ˜Jšœ˜JšœA˜AJšœœ˜Jšœ˜Jšœ˜—J˜Jšœœ˜4JšœA˜AJšœ2˜2Jšœ&Ÿ"˜HJšœ*˜*Jšœ˜—J˜šžœœœ˜'Jšœ"˜"Jšœ"˜"Jšœ(˜(Jšœ-˜-Jšœœ ˜Jšœœ˜J˜Jšœ'˜'Jšœ&˜&šœœ˜Jšœ˜Jšœ&˜&J˜—JšœA˜AJ˜šœœ˜Jšœ6˜6Jšœ6˜6Jšœ)˜)Jšœ)˜)Jšœ˜J˜—J˜Jšœ&Ÿ˜CJšœ1˜1Jšœ$˜$J˜šœŸ ˜$Jšœ˜Jšœœ ˜J˜Jšœ˜Jšœ œœ˜Jšœ˜JšœA˜AJšœ&˜&Jšœ˜Jšœ˜—J˜Jšœœ˜4JšœA˜AJ˜Jšœ2˜2Jšœ&Ÿ"˜HJšœ)˜)Jšœ˜—J˜šžœœœ˜$Jšœœ˜9Jšœœ˜9Jšœ(˜(Jšœ-˜-Jšœœ ˜J˜Jšœ'˜'šœœ˜Jšœœ˜Jšœ˜Jšœœ˜J˜—JšœA˜AJ˜šœœ˜Jšœ6˜6Jšœ6˜6Jšœ)˜)Jšœ)˜)Jšœ˜J˜—J˜Jšœ&Ÿ˜EJšœ2˜2Jšœ$˜$J˜šœŸ ˜$Jšœ˜Jšœœ˜J˜JšœŸ˜0Jšœ œœ˜Jšœ˜Jšœœ˜JšœA˜AJšœ˜Jšœ˜—J™Jšœœ˜4JšœA˜AJ˜Jšœ&Ÿ ˜FJšœ1˜1Jšœ)˜)Jšœ˜J˜—šžœœ=˜RJšœœ*˜2JšœL˜LJšœœ˜8šœ œ ˜Jšœ˜Jšœ(˜(Jšœ&˜&Jšœ(˜(Jšœœ˜#—J˜—J˜šž œœœœ˜JJšœœœœ˜2Jšœ˜ J˜—J˜šž œœ1œ˜IJšœœœœ˜2Jšœ ˜ J˜—J˜šžœœœœ˜HJšœœ˜ šœœœŸ˜7Jšœ œ˜$—Jšœœœœ˜)J˜—J˜šžœœœ˜Dšœ˜JšœœŸ˜6Jšœœ˜/—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˜——…—3ψG±