DIRECTORY Ascii USING [BS, CR, FF, SP, TAB], Basics USING [bitsPerWord], BasicTime USING [earliestGMT, FromPupTime, GMT, Now, Period], Convert USING [RopeFromTime], File USING [GetVolumeName, SystemVolume, Volume], IO USING [ card, CreateStream, CreateStreamProcs, PutFR, STREAM, StreamProcs, time], KeyboardFace USING [KeyBits, KeyStation], Keys USING [KeyBits], PrincOps USING [BBptr, BBTableSpace, BitAddress], PrincOpsUtils USING [AlignedBBTable, BITAND, BITBLT, Codebase, MyGlobalFrame], Process USING [ Abort, DisableTimeout, Pause, priorityForeground, priorityRealTime, SecondsToTicks, SetPriority, SetTimeout, Ticks], Rope USING [Fetch, Length, ROPE], SimpleTerminal USING [], SystemVersion USING [bootFileDate, release], Terminal USING [ Current, GetBitBltTable, GetBWBitmapState, GetKeys, GetMousePosition, Position, RegisterNotifier, Select, SetBWBackground, SetBWBitmapState, SetBWCursorPosition, SetMousePosition, SwapNotifier, Virtual, WaitForBWVerticalRetrace]; SimpleTerminalImpl: MONITOR LOCKS m USING m: POINTER TO MONITORLOCK IMPORTS BasicTime, Convert, File, IO, PrincOpsUtils, Process, Rope, SystemVersion, Terminal EXPORTS SimpleTerminal = BEGIN ROPE: TYPE = Rope.ROPE; State: TYPE = {off, turningOn, on, turningOff, joining}; terminalLock: MONITORLOCK; state: State _ off; stateChange: CONDITION _ [timeout: 0]; terminal: Terminal.Virtual _ NIL; turnOnCount: NAT _ 0; originalTerminal: Terminal.Virtual; inStream, outStream: IO.STREAM _ NIL; keyboardWatcher, timePainter: PROCESS; TurnOn: PUBLIC SAFE PROC [nameStripe: ROPE _ NIL] RETURNS [in, out: IO.STREAM] = TRUSTED { TurnOnEntryA: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [turnOnNeeded: BOOL] = -- INLINE -- { oldState: State = state; DO SELECT state FROM off => { IF turnOnCount ~= 0 THEN ERROR; originalTerminal _ Terminal.Current[]; state _ turningOn; EXIT }; turningOff => { state _ on; EXIT }; on => EXIT; ENDCASE; WAIT stateChange; ENDLOOP; [] _ terminal.Select[]; turnOnCount _ turnOnCount.SUCC; IF oldState ~= state THEN BROADCAST stateChange; RETURN[state ~= on] }; TurnOnEntryB: ENTRY PROC [m: POINTER TO MONITORLOCK] = -- INLINE -- { state _ on; BROADCAST stateChange; }; IF TurnOnEntryA[@terminalLock] THEN { IF nameStripe = NIL THEN nameStripe _ IO.PutFR["Cedar %g.%g.%g of %t", IO.card[SystemVersion.release.major], IO.card[SystemVersion.release.minor], IO.card[SystemVersion.release.patch], IO.time[BasicTime.FromPupTime[SystemVersion.bootFileDate]] ]; InitScreen[nameStripe]; keyboardWatcher _ FORK ProcessKeyboard[]; inStream _ IO.CreateStream[inStreamProcs, NIL]; outStream _ IO.CreateStream[outStreamProcs, NIL]; TurnOnEntryB[@terminalLock]; }; RETURN [inStream, outStream] }; TurnOff: PUBLIC SAFE PROC = TRUSTED { TurnOffEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [shutdown: BOOL] = -- INLINE -- { DO SELECT state FROM off => RETURN[FALSE]; on => EXIT; ENDCASE => WAIT stateChange; ENDLOOP; IF (shutdown _ ((turnOnCount _ turnOnCount.PRED) = 0)) THEN { state _ turningOff; BROADCAST stateChange; }; }; selected: BOOL = Terminal.Current[] = terminal; wantPreviousTerminal: BOOL = selected AND originalTerminal ~= terminal AND originalTerminal.GetBWBitmapState[] = displayed; IF TurnOffEntry[@terminalLock] AND (~selected OR wantPreviousTerminal) THEN { DoShutDown[terminal, going, NIL]; IF wantPreviousTerminal THEN [] _ originalTerminal.Select[]; }; }; DoShutDown: Terminal.SwapNotifier = TRUSTED { DoShutDownEntryA: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [needed: BOOL] = INLINE { IF (needed _ state = turningOff) THEN { state _ joining; BROADCAST stateChange; }; }; DoShutDownEntryB: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { state _ off; BROADCAST stateChange; }; IF action = going AND DoShutDownEntryA[@terminalLock] THEN { Process.Abort[keyboardWatcher]; JOIN keyboardWatcher; JOIN timePainter; inStream _ outStream _ NIL; [] _ terminal.SetBWBitmapState[none]; DoShutDownEntryB[@terminalLock]; }; }; InputTimeout: PUBLIC SAFE SIGNAL = CODE; SetInputTimeout: PUBLIC SAFE PROC [ticks: Process.Ticks] = TRUSTED { SetInputTimeoutEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { IF ticks = 0 THEN Process.DisableTimeout[@charactersAvailable] ELSE Process.SetTimeout[@charactersAvailable, ticks]; }; SetInputTimeoutEntry[@keyboardLock]; }; EnableCursorTracking: PUBLIC SAFE PROC = TRUSTED { EnableEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { IF (noTrack _ noTrack - 1) = 0 THEN doBlink _ TRUE; }; EnableEntry[@screenLock]; }; DisableCursorTracking: PUBLIC SAFE PROC = TRUSTED { DisableEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { IF (noTrack _ noTrack + 1) ~= 0 THEN doBlink _ FALSE; }; DisableEntry[@screenLock]; }; KeyArray: TYPE = MACHINE DEPENDENT RECORD [ SELECT OVERLAID * FROM nb => [namedBits: Keys.KeyBits], ub => [unnamedBits: KeyboardFace.KeyBits], wds => [words: ARRAY [0..SIZE[Keys.KeyBits]) OF WORD], ENDCASE ]; KeyItem: TYPE = RECORD [ letter: BOOL, shiftCode: CHAR [0C..177C], normalCode: CHAR [0C..377C] ]; KeyStation: TYPE = KeyboardFace.KeyStation; lowestInterestingKey: KeyStation = 16; highestInterestingKey: KeyStation = 75; KeyTable: ARRAY [lowestInterestingKey..highestInterestingKey] OF KeyItem = [ [FALSE, 45C, 65C], -- [16: k16] Five %,5 [FALSE, 44C, 64C], -- [17: k12] Four $,4 [FALSE, 176C, 66C], -- [18: k20] Six ~,6 [TRUE, 105C, 145C], -- [19: k10] E E,e [FALSE, 46C, 67C], -- [20: k24] Seven &,7 [TRUE, 104C, 144C], -- [21: k11] D D,d [TRUE, 125C, 165C], -- [22: k26] U U,u [TRUE, 126C, 166C], -- [23: k17] V V,v [FALSE, 51C, 60C], -- [24: k36] Zero ),0 [TRUE, 113C, 153C], -- [25: k31] K K,k [FALSE, 30C, 55C], -- [26: k40] Dash `,- [TRUE, 120C, 160C], -- [27: k38] P P,p [FALSE, 77C, 57C], -- [28: k41] Slash ?,/ [FALSE, 174C, 134C], -- [29: T9] BackSlash |,\ [FALSE, 12C, 12C], -- [30: L6] LF LF [FALSE, 10C, 10C], -- [31: A2] BS BS [FALSE, 43C, 63C], -- [32: k8] Three #,3 [FALSE, 100C, 62C], -- [33: k4] Two @,2 [TRUE, 127C, 167C], -- [34: k6] W W,w [TRUE, 121C, 161C], -- [35: k2] Q Q,q [TRUE, 123C, 163C], -- [36: k7] S S,s [TRUE, 101C, 141C], -- [37: k3] A A,a [FALSE, 50C, 71C], -- [38: k32] Nine (,9 [TRUE, 111C, 151C], -- [39: k30] I I,i [TRUE, 130C, 170C], -- [40: k9] X X,x [TRUE, 117C, 157C], -- [41: k34] O O,o [TRUE, 114C, 154C], -- [42: k35] L L,l [FALSE, 74C, 54C], -- [43: k33] Comma <,, [FALSE, 42C, 47C], -- [44: k43] Quote ",' [FALSE, 175C, 135C], -- [45: k45] RightBracket },] [FALSE, 0C, 0C], -- [46: R11] Spare2 [FALSE, 0C, 0C], -- [47: R6] Spare1 [FALSE, 41C, 61C], -- [48: k1] One !,1 [FALSE, 33C, 33C], -- [49: T2] ESC ESC [FALSE, 11C, 11C], -- [50: A1] TAB TAB [TRUE, 106C, 146C], -- [51: k15] F F,f [FALSE, 0C, 0C], -- [52: L11] Ctrl [TRUE, 103C, 143C], -- [53: k13] C C,c [TRUE, 112C, 152C], -- [54: k27] J J,j [TRUE, 102C, 142C], -- [55: k21] B B,b [TRUE, 132C, 172C], -- [56: k5] Z Z,z [FALSE, 0C, 0C], -- [57: A5] LeftShift [FALSE, 76C, 56C], -- [58: k37] Period >,. [FALSE, 72C, 73C], -- [59: k39] SemiColon :,; [FALSE, 15C, 15C], -- [60: A4] RETURN CR [FALSE, 136C, 137C], -- [61: k46] Arrow ^,_ [FALSE, 177C, 177C], -- [62: L3] DEL DEL [FALSE, 0C, 0C], -- [63: L9] FL3 [TRUE, 122C, 162C], -- [64: k14] R R,r [TRUE, 124C, 164C], -- [65: k18] T T,t [TRUE, 107C, 147C], -- [66: k19] G G,g [TRUE, 131C, 171C], -- [67: k22] Y Y,y [TRUE, 110C, 150C], -- [68: k23] H H,h [FALSE, 52C, 70C], -- [69: k28] Eight *,8 [TRUE, 116C, 156C], -- [70: k25] N N,n [TRUE, 115C, 155C], -- [71: k29] M M,m [FALSE, 0C, 0C], -- [72: A3] Lock [FALSE, 40C, 40C], -- [73: A7] Space SP [FALSE, 173C, 133C], -- [74: k42] LeftBracket {,[ [FALSE, 53C, 75C]]; -- [75: k44] Equal +,= -- [76: A6] RightShift -- [77: R12] Spare3 -- [78: L12] FL4 -- [79: R1] FR5 keyboardLock: MONITORLOCK; charactersAvailable: CONDITION; in, out: NAT; buffer: PACKED ARRAY NAT[0..50) OF CHAR; ProcessKeyboard: PROC = { old, new: KeyArray; blinkCount: NAT _ 33; GoAway: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOL] = INLINE { RETURN[state = joining]}; TrackCursor: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { mouse: Terminal.Position _ terminal.GetMousePosition[]; IF noTrack > 0 THEN RETURN; mouse.x _ MIN[MAX[0, mouse.x], terminal.bwWidth]; mouse.y _ MIN[MAX[0, mouse.y], terminal.bwHeight]; terminal.SetBWCursorPosition[mouse]; terminal.SetMousePosition[mouse] }; MakeCtrlChar: PROC [c: CHAR] RETURNS [CHAR] = INLINE { RETURN[LOOPHOLE[PrincOpsUtils.BITAND[c, 37B]]] }; AddToBuffer: ENTRY PROC [m: POINTER TO MONITORLOCK, c: CHAR] = INLINE { newin: NAT _ in + 1; IF newin = buffer.LENGTH THEN newin _ 0; IF newin ~= out THEN {buffer[in] _ c; in _ newin}; }; in _ out _ 0; Process.SetPriority[Process.priorityRealTime]; old.namedBits _ terminal.GetKeys[]; UNTIL GoAway[@terminalLock] DO NotifyCharsAvailable: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { BROADCAST charactersAvailable}; charsSeen: BOOL _ FALSE; terminal.WaitForBWVerticalRetrace[ ! ABORTED => CONTINUE]; new.namedBits _ terminal.GetKeys[]; TrackCursor[@screenLock]; IF (blinkCount _ blinkCount - 1) = 0 THEN {BlinkCursor[]; blinkCount _ 34}; FOR i: NAT IN [0..SIZE[KeyArray]) DO IF old.words[i] ~= new.words[i] THEN FOR k: KeyStation IN [i*Basics.bitsPerWord..(i+1)*Basics.bitsPerWord) DO char: CHAR; entry: KeyItem; IF ~(k IN [lowestInterestingKey..highestInterestingKey]) THEN LOOP; IF new.unnamedBits[k] = up OR old.unnamedBits[k] = down THEN LOOP; IF (char _ (entry _ KeyTable[k]).normalCode) ~= 0C THEN { SELECT TRUE FROM new.namedBits[Ctrl] = down => char _ MakeCtrlChar[char]; new.namedBits[LeftShift] = down, new.namedBits[RightShift] = down => char _ entry.shiftCode; new.namedBits[Lock] = down AND entry.letter => char _ entry.shiftCode; ENDCASE; AddToBuffer[@keyboardLock, char]; charsSeen _ TRUE; }; ENDLOOP; ENDLOOP; IF charsSeen THEN NotifyCharsAvailable[@keyboardLock]; old _ new; ENDLOOP; }; FontRecord: TYPE = MACHINE DEPENDENT RECORD [ newStyle(0:0..0): BOOL, indexed(0:1..1): BOOL, fixed(0:2..2): BOOL, kerned(0:3..3): BOOL, pad(0:4..15): [0..7777B], min(1): CHAR, -- limits of chars in font max(2): CHAR, -- limits of chars in font maxwidth(3): NAT, length(4): NAT, ascent(5): NAT, descent(6): NAT, xoffset(7): NAT, raster(8): NAT, chars(9:0..63): SELECT OVERLAID * FROM hasBoundingBox => [ boundingBox(9:0..63): RECORD [FontBBox, FontBBoy, FontBBdx, FontBBDy: INTEGER], BBBitmap(13): ARRAY [0..0) OF WORD], noBoundingBox => [bitmap(9): ARRAY [0..0) OF WORD], ENDCASE ]; font: LONG POINTER TO FontRecord = GetFont[]; bitmap: LONG POINTER = IF font.kerned THEN @font.BBBitmap ELSE @font.bitmap; height: NAT = font.ascent + font.descent; xInSegment: LONG POINTER TO ARRAY CHAR [0C..0C) OF NAT = bitmap + font.raster*height - (font.min-0C); GetFont: PROC RETURNS [LONG POINTER] = { Gacha10Strike: ARRAY [0..1142B) OF WORD = [ -- 0-- 100000B, 000040B, 000176B, 000007B, 001136B, 000010B, 000004B, 000000B, -- 10-- 000052B, 000000B, 000001B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 20-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 30-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 40-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 50-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 60-- 000000B, 000000B, 017000B, 000040B, 120243B, 106204B, 004010B, 040100B, -- 70-- 000000B, 000002B, 034040B, 160701B, 107616B, 037070B, 070000B, 000200B, -- 100-- 010034B, 034041B, 140707B, 107637B, 016104B, 070162B, 022010B, 110434B, -- 110-- 074161B, 160707B, 144221B, 021104B, 104761B, 141007B, 000000B, 000200B, -- 120-- 000040B, 001600B, 020010B, 010401B, 140000B, 000000B, 000000B, 000000B, -- 130-- 000000B, 000000B, 000060B, 103000B, 017000B, 000040B, 120245B, 052412B, -- 140-- 004020B, 020520B, 000000B, 000002B, 042141B, 011042B, 104021B, 001104B, -- 150-- 104000B, 000400B, 004042B, 042121B, 021044B, 044020B, 021104B, 020022B, -- 160-- 042015B, 114442B, 042211B, 011041B, 004221B, 021104B, 104021B, 001001B, -- 170-- 000000B, 000200B, 000040B, 002000B, 020000B, 000400B, 040000B, 000000B, -- 200-- 000000B, 000400B, 000000B, 000000B, 000100B, 100400B, 017000B, 000040B, -- 210-- 121765B, 054412B, 004020B, 020340B, 100000B, 000004B, 046241B, 010042B, -- 220-- 104020B, 002104B, 104100B, 101000B, 002042B, 056121B, 021044B, 044020B, -- 230-- 021104B, 020022B, 102015B, 114442B, 042211B, 011041B, 004221B, 025050B, -- 240-- 050041B, 000401B, 002010B, 034260B, 160643B, 107415B, 026070B, 070440B, -- 250-- 043313B, 007054B, 032260B, 161704B, 044221B, 021104B, 174100B, 100400B, -- 260-- 017000B, 000040B, 000503B, 001004B, 000040B, 010520B, 100000B, 000004B, -- 270-- 052040B, 020704B, 107436B, 002070B, 104100B, 102017B, 101004B, 052121B, -- 300-- 161004B, 047436B, 020174B, 020023B, 102012B, 112442B, 042211B, 160601B, -- 310-- 004212B, 025020B, 050101B, 000401B, 007020B, 002311B, 011144B, 042023B, -- 320-- 031010B, 010500B, 042514B, 110462B, 046311B, 010404B, 044221B, 012104B, -- 330-- 010100B, 100406B, 057000B, 000040B, 000501B, 101012B, 100040B, 010103B, -- 340-- 160017B, 100010B, 052040B, 040044B, 104221B, 004104B, 074000B, 002000B, -- 350-- 001010B, 052211B, 011004B, 044020B, 027104B, 020022B, 042012B, 112442B, -- 360-- 074211B, 040101B, 004212B, 025020B, 020101B, 000201B, 012476B, 036211B, -- 370-- 001047B, 142021B, 021010B, 010600B, 042510B, 110442B, 042200B, 140404B, -- 400-- 042425B, 004104B, 020100B, 100411B, 117000B, 000000B, 003745B, 042321B, -- 410-- 000040B, 010000B, 100000B, 000010B, 062040B, 100047B, 140221B, 004104B, -- 420-- 004000B, 001017B, 102000B, 056371B, 011044B, 044020B, 021104B, 020422B, -- 430-- 042012B, 111442B, 040211B, 021041B, 004212B, 012050B, 020201B, 000201B, -- 440-- 002020B, 042211B, 001044B, 002021B, 021010B, 010500B, 042510B, 110442B, -- 450-- 042200B, 020404B, 042425B, 004050B, 020600B, 100300B, 017000B, 000040B, -- 460-- 001205B, 042521B, 000040B, 010000B, 101400B, 002020B, 042041B, 001040B, -- 470-- 104221B, 010104B, 104101B, 100400B, 004010B, 040211B, 011044B, 044020B, -- 500-- 021104B, 020422B, 022012B, 111442B, 040211B, 011041B, 004204B, 012104B, -- 510-- 020401B, 000101B, 002010B, 042311B, 011144B, 042023B, 021010B, 010440B, -- 520-- 042510B, 110462B, 046201B, 010444B, 141012B, 012050B, 040100B, 100400B, -- 530-- 017000B, 000040B, 001203B, 104616B, 100040B, 010000B, 000400B, 002020B, -- 540-- 034371B, 170700B, 103416B, 010070B, 070100B, 100200B, 010010B, 036211B, -- 550-- 160707B, 107620B, 016104B, 070342B, 023710B, 110434B, 040161B, 010701B, -- 560-- 003404B, 012104B, 020761B, 000101B, 002000B, 036260B, 160643B, 102015B, -- 570-- 021010B, 010420B, 042510B, 107054B, 032200B, 160303B, 041012B, 021020B, -- 600-- 174100B, 100400B, 000000B, 000000B, 000001B, 000000B, 000020B, 020000B, -- 610-- 000400B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 100000B, -- 620-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 630-- 000040B, 000000B, 000000B, 000000B, 000001B, 000001B, 000000B, 000000B, -- 640-- 000000B, 000001B, 000000B, 110000B, 000000B, 000040B, 002000B, 000000B, -- 650-- 000000B, 000020B, 000100B, 100400B, 000000B, 000000B, 000000B, 000000B, -- 660-- 000020B, 020000B, 001000B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 670-- 000001B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 700-- 000000B, 000000B, 000030B, 000000B, 000000B, 000000B, 000001B, 000001B, -- 710-- 000000B, 000000B, 000000B, 000016B, 000000B, 060000B, 000000B, 000040B, -- 720-- 002000B, 000000B, 000000B, 000140B, 000100B, 100400B, 000000B, 000000B, -- 730-- 000000B, 000000B, 000010B, 040000B, 000000B, 000000B, 000000B, 000000B, -- 740-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 750-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 760-- 000001B, 140007B, 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, -- 770-- 000000B, 000000B, 000000B, 000000B, 000000B, 000000B, 000060B, 103000B, --1000-- 012602B, 000000B, 000007B, 000016B, 000025B, 000034B, 000043B, 000052B, --1010-- 000061B, 000070B, 000077B, 000106B, 000115B, 000124B, 000133B, 000142B, --1020-- 000151B, 000160B, 000167B, 000176B, 000205B, 000214B, 000223B, 000232B, --1030-- 000241B, 000250B, 000257B, 000266B, 000275B, 000304B, 000313B, 000322B, --1040-- 000331B, 000340B, 000347B, 000356B, 000365B, 000374B, 000403B, 000412B, --1050-- 000421B, 000430B, 000437B, 000446B, 000455B, 000464B, 000473B, 000502B, --1060-- 000511B, 000520B, 000527B, 000536B, 000545B, 000554B, 000563B, 000572B, --1070-- 000601B, 000610B, 000617B, 000626B, 000635B, 000644B, 000653B, 000662B, --1100-- 000671B, 000700B, 000700B, 000707B, 000716B, 000725B, 000734B, 000743B, --1110-- 000752B, 000761B, 000770B, 000777B, 001006B, 001015B, 001024B, 001033B, --1120-- 001042B, 001051B, 001060B, 001067B, 001076B, 001105B, 001114B, 001123B, --1130-- 001132B, 001141B, 001150B, 001157B, 001166B, 001175B, 001204B, 001213B, --1140-- 001222B, 001230B]; p: LONG POINTER TO ARRAY [0..1142B) OF WORD _ PrincOpsUtils.Codebase[PrincOpsUtils.MyGlobalFrame[]]; DO IF p[0] = Gacha10Strike[0] AND p^ = Gacha10Strike THEN RETURN[p]; p _ p+1; ENDLOOP; }; screenLock: MONITORLOCK; noTrack: CARDINAL _ 0; doBlink: BOOL _ FALSE; bbTable: PrincOps.BBTableSpace; bbPtr: PrincOps.BBptr = PrincOpsUtils.AlignedBBTable[@bbTable]; charPos, line, nCharPos, nLines: NAT; usableHeight, usableWidth: NAT; firstLine, thisLine: PrincOps.BitAddress; bitsPerTextLine: NAT; -- = screenWidth*font.height nameStripeOrg: PrincOps.BitAddress; nameStripeWidth: NAT; GetBitAddress: PROC [p: LONG POINTER, o: LONG CARDINAL] RETURNS [PrincOps.BitAddress] = { RETURN[[p+o/16, 0, o MOD 16]] }; InitScreen: PROC [nameStripe: ROPE] = { borderWidth: NAT = 16; boxWidth: NAT = 1; marginWidth: NAT = 8; org: PrincOps.BitAddress; InitBitmap: PROC = { zero: CARDINAL _ 0; bbPtr.src _ [@zero, 0, 0]; bbPtr.srcDesc _ [gray[[0, 0, 0, 0]]]; bbPtr.flags _ [gray: TRUE]; PrincOpsUtils.BITBLT[bbPtr]; }; MakeBorder: PROC [w: NAT] = { stipple: ARRAY [0..3] OF CARDINAL _ [104210B, 104210B, 021042B, 021042B]; bbPtr.src _ [@stipple, 0, 0]; bbPtr.srcDesc _ [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 3]]]; bbPtr.flags _ [gray: TRUE]; bbPtr.dst _ org; bbPtr.width _ usableWidth; bbPtr.height _ w; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.dst _ GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*(usableHeight-w)]; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.dst _ GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w]; bbPtr.width _ w; bbPtr.height _ usableHeight - w - w; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.dst _ GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+usableWidth-w]; PrincOpsUtils.BITBLT[bbPtr]; org _ GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+w]; usableHeight _ usableHeight - w - w; usableWidth _ usableWidth - w - w; }; MakeBox: PROC [w: NAT] = { black: CARDINAL _ 177777B; bbPtr.src _ [@black, 0, 0]; bbPtr.srcDesc _ [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: w-1]]]; bbPtr.flags _ [gray: TRUE]; bbPtr.dst _ org; bbPtr.width _ usableWidth; bbPtr.height _ w; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.dst _ GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*(usableHeight-w)]; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.dst _ GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w]; bbPtr.width _ w; bbPtr.height _ usableHeight-w-w; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.dst _ GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+usableWidth-w]; PrincOpsUtils.BITBLT[bbPtr]; org _ GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*w+w]; usableHeight _ usableHeight - w - w; usableWidth _ usableWidth - w - w; }; MakeNameStripe: PROC [nameStripe: ROPE] = { black: CARDINAL _ 177777B; systemVolume: File.Volume = File.SystemVolume[]; volumeLabel: ROPE = IF systemVolume = NIL THEN "No System Volume" ELSE File.GetVolumeName[systemVolume]; nameStripeOrg _ org; nameStripeWidth _ usableWidth; bbPtr.dst _ GetBitAddress[org.word, org.bit+marginWidth]; bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.height _ height; bbPtr.width _ font.maxwidth; bbPtr.flags _ []; FOR i: CARDINAL IN CARDINAL[0..nameStripe.Length[]) DO bbPtr.src _ GetBitAddress[bitmap, xInSegment[nameStripe.Fetch[i]]]; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.dst _ GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth]; ENDLOOP; bbPtr.dst _ GetBitAddress[org.word, org.bit+usableWidth-marginWidth-volumeLabel.Length[]*font.maxwidth]; FOR i: CARDINAL IN CARDINAL[0..volumeLabel.Length[]) DO bbPtr.src _ GetBitAddress[bitmap, xInSegment[volumeLabel.Fetch[i]]]; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.dst _ GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth]; ENDLOOP; bbPtr.src _ [@black, 0, 0]; bbPtr.srcDesc _ [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]]; bbPtr.flags _ [gray: TRUE, dstFunc: xor]; bbPtr.dst _ org; bbPtr.width _ usableWidth; bbPtr.height _ height; PrincOpsUtils.BITBLT[bbPtr]; org _ GetBitAddress[org.word, org.bit+LONG[bbPtr.dstBpl]*height]; usableHeight _ usableHeight - height; timePainter _ FORK MaintainTime[]; }; [] _ terminal.SetBWBackground[white]; [] _ terminal.SetBWBitmapState[allocated]; bbPtr^ _ terminal.GetBitBltTable[]; org _ bbPtr.dst; usableHeight _ bbPtr.height; usableWidth _ bbPtr.width; InitBitmap[]; MakeBorder[borderWidth]; MakeBox[boxWidth]; MakeNameStripe[nameStripe]; [] _ terminal.SetBWBitmapState[displayed]; usableHeight _ usableHeight - 2*marginWidth; usableWidth _ usableWidth - 2*marginWidth; bitsPerTextLine _ bbPtr.dstBpl*height; firstLine _ GetBitAddress[org.word, org.bit+LONG[marginWidth]*bbPtr.dstBpl+marginWidth]; nCharPos _ usableWidth/font.maxwidth; nLines _ usableHeight/height; InitPainting[]; }; MaintainTime: PROC = { GoAway: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOLEAN] = INLINE { RETURN[state = joining]}; bbTable: PrincOps.BBTableSpace; bbPtr: PrincOps.BBptr = PrincOpsUtils.AlignedBBTable[@bbTable]; black: CARDINAL _ 177777B; date: ROPE _ NIL; dateLength: NAT _ 0; secondsOrg: PrincOps.BitAddress; lastMinutes: INT _ 0; SetToBlacken: PROC = { bbPtr.src _ [@black, 0, 0]; bbPtr.srcDesc _ [gray[[yOffset: 0, widthMinusOne: 0, heightMinusOne: 0]]]; bbPtr.flags _ [gray: TRUE]}; PaintTime: PROC = { current: BasicTime.GMT = BasicTime.Now[]; secondsSinceGenesis: INT = BasicTime.Period[from: BasicTime.earliestGMT, to: current]; minutesSinceGenesis: INT = secondsSinceGenesis/60; IF lastMinutes = minutesSinceGenesis THEN { seconds: [0..59] _ secondsSinceGenesis MOD 60; SetToBlacken[]; bbPtr.dst _ secondsOrg; bbPtr.width _ 2*font.maxwidth; bbPtr.height _ height; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.width _ font.maxwidth; bbPtr.flags _ [srcFunc: complement]; bbPtr.src _ GetBitAddress[bitmap, xInSegment['0+seconds/10]]; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.dst _ GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth]; bbPtr.src _ GetBitAddress[bitmap, xInSegment['0+seconds MOD 10]]; PrincOpsUtils.BITBLT[bbPtr]} ELSE { SetToBlacken[]; bbPtr.dst _ GetBitAddress[nameStripeOrg.word, nameStripeOrg.bit+(nameStripeWidth-dateLength*font.maxwidth)/2]; bbPtr.width _ dateLength*font.maxwidth; bbPtr.height _ height; PrincOpsUtils.BITBLT[bbPtr]; date _ Convert.RopeFromTime[from: current, start: years, end: seconds, includeDayOfWeek: TRUE, useAMPM: FALSE, includeZone: TRUE]; dateLength _ date.Length[]; bbPtr.dst _ GetBitAddress[nameStripeOrg.word, nameStripeOrg.bit+(nameStripeWidth-dateLength*font.maxwidth)/2]; secondsOrg _ GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+(dateLength-2)*font.maxwidth]; bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.width _ font.maxwidth; bbPtr.flags _ [srcFunc: complement]; FOR i: CARDINAL IN [0..dateLength) DO bbPtr.src _ GetBitAddress[bitmap, xInSegment[date.Fetch[i]]]; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.dst _ GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit+font.maxwidth]; ENDLOOP; }; lastMinutes _ minutesSinceGenesis; }; Process.SetPriority[Process.priorityForeground]; bbPtr^ _ terminal.GetBitBltTable[]; UNTIL GoAway[@terminalLock] DO PaintTime[]; Process.Pause[Process.SecondsToTicks[1]]; ENDLOOP; }; InitPainting: PROC = { charPos _ 0; line _ 1; bbPtr.dst _ thisLine _ firstLine; bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.height _ height; bbPtr.width _ font.maxwidth; bbPtr.flags _ []; }; ClearThisChar: INTERNAL PROC = { zero: CARDINAL _ 0; bbPtr.src _ [@zero, 0, 0]; bbPtr.srcDesc _ [gray[[0, 0, 0, 0]]]; bbPtr.flags _ [gray: TRUE]; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.flags _ []; }; DisplayChar: INTERNAL PROC [c: CHAR] = { Backup: INTERNAL PROC = { t: NAT = bbPtr.dst.bit+16-font.maxwidth; IF charPos=0 THEN RETURN; charPos _ charPos - 1; bbPtr.dst.word _ bbPtr.dst.word + t/16 - 1; bbPtr.dst.bit _ t MOD 16; }; ClearScreen: PROC = { zero: CARDINAL _ 0; bbPtr.dst _ firstLine; bbPtr.src _ [@zero, 0, 0]; bbPtr.srcDesc _ [gray[[0, 0, 0, 0]]]; bbPtr.flags _ [gray: TRUE]; bbPtr.height _ usableHeight; bbPtr.width _ usableWidth; PrincOpsUtils.BITBLT[bbPtr]; InitPainting[]; }; Newline: INTERNAL PROC = { IF line < nLines THEN {thisLine _ GetBitAddress[thisLine.word, thisLine.bit+bitsPerTextLine]; line _ line+1} ELSE { zero: CARDINAL _ 0; sBBTable: PrincOps.BBTableSpace; sbbPtr: PrincOps.BBptr = PrincOpsUtils.AlignedBBTable[@sBBTable]; sbbPtr^ _ [ dst: firstLine, dstBpl: bbPtr.dstBpl, src: GetBitAddress[firstLine.word, firstLine.bit+bitsPerTextLine], srcDesc: [srcBpl[bbPtr.dstBpl]], flags: [direction: forward], width: nCharPos*font.maxwidth, height: height*(nLines-1)]; PrincOpsUtils.BITBLT[sbbPtr]; sbbPtr^ _ [ dst: thisLine, src: [@zero, 0, 0], dstBpl: bbPtr.dstBpl, srcDesc: [gray[[0, 0, 0, 0]]], width: nCharPos*font.maxwidth, height: height, flags: [gray: TRUE]]; PrincOpsUtils.BITBLT[sbbPtr]; }; bbPtr.dst _ thisLine; charPos _ 0; }; SELECT c FROM IN (Ascii.SP..'~] => { IF ~(c IN [font.min..font.max]) THEN c _ font.max + 1; bbPtr.src _ GetBitAddress[bitmap, xInSegment[c]]; PrincOpsUtils.BITBLT[bbPtr]; }; Ascii.SP => NULL; Ascii.CR => {Newline[]; RETURN}; Ascii.BS => {Backup[]; ClearThisChar[]; RETURN}; Ascii.TAB => {UNTIL charPos MOD 8 = 0 DO DisplayChar[Ascii.SP]; ENDLOOP; RETURN}; Ascii.FF => {ClearScreen[]; RETURN}; IN [0C..Ascii.SP) => {DisplayChar['^]; DisplayChar[c+('A-1C)]; RETURN}; ENDCASE => RETURN; IF (charPos _ charPos + 1) >= nCharPos THEN Newline[] ELSE bbPtr.dst _ GetBitAddress[bbPtr.dst.word, bbPtr.dst.bit + font.maxwidth]; }; BlinkCursor: PROC = { BlinkCursorEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = { blinker: CARDINAL _ 60000B; IF doBlink THEN { bbPtr.src _ [@blinker, 0, 0]; bbPtr.srcDesc _ [gray[[0, 0, 0, 0]]]; bbPtr.flags _ [gray: TRUE, dstFunc: xor]; PrincOpsUtils.BITBLT[bbPtr]; bbPtr.srcDesc _ [srcBpl[font.raster*16]]; bbPtr.flags _ []; }; }; IF doBlink THEN BlinkCursorEntry[@screenLock]; }; CreateStreams: PROC = { }; CharsAvail: SAFE PROC [self: IO.STREAM, wait: BOOL] RETURNS [INT] = TRUSTED { CharsAvailEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOL] = INLINE {RETURN[in ~= out]}; RETURN[IF CharsAvailEntry[@keyboardLock] THEN 1 ELSE 0] }; EndOf: SAFE PROC [self: IO.STREAM] RETURNS [BOOL] = CHECKED {RETURN[FALSE]}; GetChar: SAFE PROC [self: IO.STREAM] RETURNS [c: CHAR] = TRUSTED { GetEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] RETURNS [BOOL] = INLINE { ENABLE UNWIND => NULL; WHILE in = out DO WAIT charactersAvailable; IF in = out THEN RETURN[FALSE]; ENDLOOP; c _ buffer[out]; IF (out _ out + 1) = buffer.LENGTH THEN out _ 0; RETURN[TRUE] }; UNTIL GetEntry[@keyboardLock] DO SIGNAL InputTimeout; ENDLOOP; }; PutChar: SAFE PROC [self: IO.STREAM, char: CHAR] = TRUSTED { PutCharEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { doBlink _ FALSE; ClearThisChar[]; -- to get rid of possible blinker DisplayChar[char]; doBlink _ TRUE; }; PutCharEntry[@screenLock]; }; EraseChar: SAFE PROC [self: IO.STREAM, char: CHAR] = TRUSTED { EraseCharEntry: ENTRY PROC [m: POINTER TO MONITORLOCK] = INLINE { doBlink _ FALSE; ClearThisChar[]; -- to get rid of possible blinker DisplayChar[Ascii.BS]; doBlink _ TRUE; }; EraseCharEntry[@screenLock]; }; inStreamProcs: REF IO.StreamProcs; outStreamProcs: REF IO.StreamProcs; Initialize: ENTRY PROC [m: POINTER TO MONITORLOCK] = { IF ~font.newStyle OR font.indexed OR ~(font.min IN [0C..177C]) OR ~(font.max+1 IN [0C..177C]) THEN ERROR; inStreamProcs _ IO.CreateStreamProcs[ variety: $input, class: $SimpleTerminal, getChar: GetChar, endOf: EndOf, charsAvail: CharsAvail ]; outStreamProcs _ IO.CreateStreamProcs[ variety: $output, class: $SimpleTerminal, putChar: PutChar, eraseChar: EraseChar ]; terminal _ Terminal.Current[]; terminal.RegisterNotifier[DoShutDown]; }; Initialize[@terminalLock]; END. nSimpleTerminalImpl.mesa Derived from SimpleTTY.mesa last edited by Forrest January 6, 1981 7:28 PM last edited by Levin on December 15, 1983 4:40 pm last edited by McGregor on April 22, 1982 4:23 pm Last Edited by: Birrell, July 7, 1983 3:20 pm Last Edited by: MBrown, September 17, 1983 8:18 pm Constraints to satisfy externally: A statevector is needed at priority 6 for the keyboard watching process, at least until facilities exist to let it allocate one itself. If keyboard watcher is to avoid faults, its code and global frame should be pinned. Global variables protected by "terminalLock" Exported to SimpleTerminal Shutdown was deferred and is still pending. We simply cancel it. The following is intended to be TRUE when the terminal that was current at the time TurnOn was called has a screen displayed. It is therefore more interesting than ours and we should shut our screen down now and make its visible again. If there wasn't a visible screen when TurnOn was called and we are still the current terminal, we leave our screen visible until someone else invokes Terminal.Select. Keyboard Definitions -- [bit number: KeyStation] KeyName Ascii codes -- [0: ] -- [1: ] -- [2: ] -- [3: ] -- [4: ] -- [5: ] -- [6: ] -- [7: ] -- [8: KS1] Keyset1 -- [9: KS2] Keyset2 -- [10: KS3] Keyset3 -- [11: KS4] Keyset4 -- [12: KS5] Keyset5 -- [13: M1] Red -- [14: M3] Blue -- [15: M2] Yellow -- [80: R5] R5 -- [81: R9] R9 -- [82: L10] L10 -- [83: L7] L7 -- [84: L4] L4 -- [85: L1] L1 -- [86: A9] A9 -- [87: R10] R10 -- [88: A8] A8 -- [89: L8] L8 -- [90: L5] L5 -- [91: L2] L2 -- [92: R2] R2 -- [93: R7] R7 -- [94: R4] R4 -- [95: D2] D2 -- [96: D1] D1 -- [97: k48] Key48 -- [98: T1] T1 -- [99: T3] T3 -- [100: T4] T4 -- [101: T5] T5 -- [102: T6] T6 -- [103: T7] T7 -- [104: T8] T8 -- [105: T10] T10 -- [106: R3] R3 -- [107: k47] Key47 -- [108: A10] A10 -- [109: R8] R8 -- [110: A11] -- [111: A12] Keyboard Implementation Global variables protected by "keyboardLock" Font Definitions Note: Even though it doesn't say so in its strike header, this is a fixed-width font. Bitter experience has shown that not all Gacha10.strike font files are, in fact, fixed width. You need to painstakingly examine the xInSegment table for the truth. The following hack finds the address of constant array Gacha10Strike in the code segment and returns it. This is slightly unethical, but it saves duplicate allocation. Display Implementation Global variables protected by "screenLock" bbPtr.dst, bbPtr.height, and bbPtr.width are set up bbPtr.dstBpl never changes top stripe bottom stripe left side right side top stripe bottom stripe left side right side paint version text paint volume name overlay with stripe now display the bitmap bbPtr.dst & bbPtr.height still OK set up standard arguments for character painting bbPtr.dstBpl already set up bbPtr.src set when proc called IO Streams implementation Initialization Ê!»˜Jšœ™JšœL™LJšœ1™1Jšœ1™1Jšœ-™-Jšœ2™2J˜šÏk ˜ Jš œœœœœœœ˜#Jšœœ˜Jšœ œœ˜=Jšœœ˜Jšœœ'˜1šœœ˜ Jšœ.œ˜I—Jšœ œ˜)Jšœœ ˜Jšœ œ#˜1Jšœœœœ˜Nšœœ˜Jšœt˜t—Jšœœœ˜!Jšœœ˜Jšœœ˜,šœ œ˜Jšœå˜å—J˜—š œ œœœœ ˜CJšœœ7˜[Jšœ˜J˜—Jš˜J˜Jšœœœ˜J˜šœ"™"Jšœ‡™‡J™S—J˜Jšœœ-˜8J˜Jšœ,™,J˜Jšœ œ˜J˜Jšœ˜Jšœ œ˜&J˜Jšœœ˜!Jšœ œ˜J˜Jšœ#˜#J˜Jšœœœœ˜%J˜Jšœœ˜&J˜J™Jšœ™J˜šÏnœœœœœœœ œœœ˜Zš ž œœœœœ œ˜4JšœœÏc œ˜-J˜š˜šœ˜šœ˜Jšœœœ˜Jšœ&˜&Jšœ˜Jš˜J˜—šœ˜JšœA™AJ˜ Jš˜Jšœ˜—Jšœœ˜ Jšœ˜—Jšœ ˜Jšœ˜—Jšœ˜Jšœœ˜Jšœœ œ ˜0Jšœ ˜Jšœ˜—šž œœœœœ œŸ œ˜EJšœ ˜ Jš œ ˜Jšœ˜—šœœ˜%šœœ˜šœ œ˜-Jšœ#˜%Jšœ#˜%Jšœ#˜%Jšœ8˜:J˜——J˜Jšœœ˜)Jšœ œœ˜/Jšœ œœ˜1Jšœ˜J˜—Jšœ˜Jšœ˜—J˜š žœœœœœ˜%š ž œœœœœ œ˜4Jšœ œŸ œ˜)š˜šœ˜Jšœœœ˜Jšœœ˜ Jšœœ ˜—Jšœ˜—šœ)œœ˜=Jšœ˜Jš œ ˜Jšœ˜—J˜—Jšœ œ!˜/Jšœ–™–šœœ˜Jšœ ˜ Jšœœ1˜Q—šœœ œœ˜MJšœœ˜!Jšœœ ˜Jšœ1˜5Jšœ˜—J˜$J˜J˜—J˜š žœœœœœ˜2šž œœœœœ œœ˜>Jšœœ ˜3Jšœ˜—J˜J˜J˜—š žœœœœœ˜3šž œœœœœ œœ˜?Jšœœ ˜5Jšœ˜—J˜J˜J˜J˜—J™Jšœ™J˜š œ œœ œœ˜+šœœ˜Jšœ ˜ Jšœ*˜*Jš œœœœœ˜6Jš˜—Jšœ˜J˜—šœ œœ˜Jšœœ˜ Jšœ œ ˜Jšœ œ ˜Jšœ˜J˜—Jšœ œ˜+J˜J˜&J˜'J˜šœ œ/œ ˜LJ™JJ™#J™#J™#J™#J™#J™#J™#J™#J™-J™-J™.J™.J™.J™)J™*J™,JšœœŸÐciÏi Ÿ˜,JšœœŸ¡ Ÿ˜,JšœœŸ¡  Ÿ˜+JšœœŸ¡  Ÿ˜)JšœœŸ¡ Ÿ˜-JšœœŸ¡  Ÿ˜)JšœœŸ¡ Ÿ Ÿ˜)JšœœŸ¡ Ÿ Ÿ˜)JšœœŸ¡ Ÿ Ÿ˜,JšœœŸ¡ Ÿ Ÿ˜)JšœœŸ¡ Ÿ Ÿ˜,JšœœŸ¡ Ÿ Ÿ˜)JšœœŸ¡ Ÿ Ÿ˜-JšœœŸ¡ Ÿ  Ÿ˜0JšœœŸ¡ Ÿ Ÿ˜(JšœœŸ¡ Ÿ Ÿ˜(JšœœŸ¡ Ÿ Ÿ˜,JšœœŸ¡ Ÿ Ÿ˜*JšœœŸ¡ Ÿ Ÿ˜(JšœœŸ¡ Ÿ Ÿ˜(JšœœŸ¡ Ÿ Ÿ˜(JšœœŸ¡ Ÿ Ÿ˜(JšœœŸ¡ Ÿ Ÿ˜,JšœœŸ¡ Ÿ Ÿ˜)JšœœŸ¡ Ÿ Ÿ˜(JšœœŸ¡ Ÿ Ÿ˜)JšœœŸ¡ Ÿ Ÿ˜)JšœœŸ¡ Ÿ Ÿ˜,JšœœŸ¡ Ÿ Ÿ˜,JšœœŸ¡ Ÿ  Ÿ˜3JšœœŸ¡ Ÿ˜)JšœœŸ¡ Ÿ˜(JšœœŸ˜*JšœœŸ˜*JšœœŸ˜*JšœœŸ˜)JšœœŸ˜'JšœœŸ˜)JšœœŸ˜)JšœœŸ˜)JšœœŸ˜(JšœœŸ˜+JšœœŸ˜.JšœœŸ˜1JšœœŸ˜,JšœœŸ˜-JšœœŸ˜*JšœœŸ˜%JšœœŸ˜)JšœœŸ˜)JšœœŸ˜)JšœœŸ˜)JšœœŸ˜)JšœœŸ˜-JšœœŸ˜)JšœœŸ˜)JšœœŸ˜&JšœœŸ˜+JšœœŸ˜3JšœœŸ˜.JšœŸ˜0JšœŸ˜-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šœ™J˜J™Jšœ,™,J˜Jšœ œ˜J˜Jšœ œ˜J˜Jšœ œ˜ Jš œœœœœœ˜(J˜šžœœ˜J˜Jšœ œ˜šžœœœœœ œœœœ˜HJšœ˜—šž œœœœœ œœ˜?J˜7Jšœ œœ˜Jšœ œœ ˜1Jšœ œœ!˜2Jšœ$˜$Jšœ ˜ Jšœ˜—š ž œœœœœœ˜6Jšœœœ ˜.Jšœ˜—šž œœœœœ œœœ˜GJšœœ˜Jšœœœ ˜(Jšœœ˜2Jšœ˜—J˜ J˜.J˜#šœ˜šžœœœœœ œœ˜GJš œ˜—Jšœ œœ˜Jšœ%œœ˜:J˜#J˜Jšœ#œ"˜Kš œœœœ ˜$šœ˜$šœœ2˜HJšœœ˜ J˜Jšœœ0œœ˜CJšœœœœ˜Bšœ1œ˜9šœœ˜šœ˜Jšœ˜—˜DJ˜—šœœ˜.J˜—Jšœ˜—J˜!Jšœ œ˜Jšœ˜—Jšœ˜——Jšœ˜—Jšœ œ%˜6J˜ Jšœ˜—Jšœ˜J˜—J™Jšœ™J˜š œ œœ œœ˜-Jšœœ˜Jšœœ˜Jšœœ˜Jšœœ˜J˜JšœœŸ˜*JšœœŸ˜*Jšœ œ˜Jšœ œ˜Jšœ œ˜Jšœ œ˜Jšœ œ˜Jšœ œ˜šœœœ˜&˜Jšœœ*œ˜OJšœœœœ˜$—Jšœœœœ˜3Jš˜—šœ˜J˜——šœœœœ˜-J˜—Jš œœœœ œœ˜LJšœœ˜)šœ œœœœœ œœ˜8Jšœ,˜,—J˜š žœœœœœ˜(JšœP™PJšœQ™QJšœN™NJšœ ™ Jšœœ œœ˜+JšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœI˜QJšŸœ˜Jšœ¨™¨š œœœœœ œœ˜-Jšœ6˜6—š˜Jšœœœœ˜AJ˜Jšœ˜—Jšœ˜J˜—J™J™J˜J˜Jšœ*™*J˜Jšœ œ˜J˜Jšœ œ˜Jšœ œœ˜J˜J˜J˜?J˜Jšœ!œ˜%Jšœœ˜J˜)JšœœŸ˜3J˜J˜#Jšœœ˜J˜J˜šž œœœœœœœ˜YJšœœ˜Jšœ˜J˜—šž œœœ˜'Jšœ œ˜Jšœ œ˜Jšœ œ˜J˜šž œœ˜Jšœœ˜Jšœ3™3J˜J˜%Jšœœ˜Jšœœ˜Jšœ˜—šž œœœ˜Jšœ œœœ(˜IJšœ™J˜J˜JJšœœ˜Jšœ ™ J˜J˜J˜Jšœœ˜Jšœ ™ Jšœ,œ!˜QJšœœ˜Jšœ ™ Jšœ,œ˜BJ˜J˜$Jšœœ˜Jšœ ™ Jšœ,œ ˜PJšœœ˜Jšœ&œ˜>J˜$J˜"J˜—šžœœœ˜Jšœœ ˜J˜J˜LJšœœ˜Jšœ ™ J˜J˜J˜Jšœœ˜Jšœ ™ Jšœ,œ!˜QJšœœ˜Jšœ ™ Jšœ,œ˜BJ˜J˜ Jšœœ˜Jšœ ™ Jšœ,œ ˜PJšœœ˜Jšœ&œ˜>J˜$J˜"J˜—šžœœœ˜+Jšœœ ˜Jšœ0˜0šœ œ˜Jšœœœ˜-Jšœ"˜&—J˜J˜Jšœ™J˜9J˜)J˜J˜J˜š œœœœ˜6J˜CJšœœ˜J˜GJšœ˜—Jšœ™˜#J˜D—š œœœœ˜7J˜DJšœœ˜J˜GJšœ˜—Jšœ™J˜J˜JJšœœ˜)J˜J˜J˜Jšœœ˜Jšœ&œ˜AJ˜%Jšœœ˜"J˜—J˜%Jšœ*˜*J˜#J˜J˜J˜J˜ J˜J˜J˜Jšœ™Jšœ*˜*J˜,J˜*J˜&˜ Jšœ œ(˜L—J˜%J˜J˜Jšœ˜J˜—šž œœ˜šžœœœœœ œœœœ˜KJšœ˜—J˜J˜?Jšœœ ˜Jšœœœ˜Jšœ œ˜J˜ Jšœ œ˜šž œœ˜J˜J˜JJšœœ˜—šž œœ˜Jšœœ˜)Jšœœ>˜VJšœœ˜2šœ#œ˜+Jšœ'œ˜.J˜J˜J˜J˜Jšœœ˜Jšœ!™!J˜)J˜J˜$J˜=Jšœœ˜J˜GJšœ8œ˜AJšœœ˜—šœ˜J˜˜-Jšœ@˜@—Jšœ'˜'J˜Jšœœ˜šœF˜FJšœœ œ œ˜;—Jšœ˜˜-Jšœ@˜@—JšœW˜WJ˜)J˜J˜$šœœœ˜%J˜=Jšœœ˜J˜GJšœ˜—J˜—Jšœ"˜"Jšœ˜—J˜0J˜#šœ˜J˜ J˜)Jš˜—šœ˜J˜——šž œœ˜Jšœ0™0J˜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˜V—šœ˜Jšœœ˜J˜ J˜A˜ J˜%J˜BJ˜=J˜:—Jšœœ ˜˜ J˜"J˜4J˜.Jšœœ˜—Jšœœ ˜Jšœ˜—J˜J˜ Jšœ˜—šœ˜ šœœ ˜Jšœœœ˜6J˜1Jšœœ˜Jšœ˜—Jšœœœ˜Jšœœœ˜ Jšœœ œ˜0Jšœœœ œœœœœ˜QJšœœœ˜$Jšœ œ/œ˜GJšœœ˜—Jšœ%œ ˜5JšœJ˜NJšœ˜J˜—šž œœ˜š žœœœœœ œ˜Jšœ˜J˜—šžœœœœœœœ˜<šž œœœœœ œœ˜?Jšœ œ˜JšœŸ!˜3Jšœ˜Jšœ œ˜Jšœ˜—J˜Jšœ˜J˜—šž œœœœœœœ˜>šžœœœœœ œœ˜AJšœ œ˜JšœŸ!˜3Jšœœ˜Jšœ œ˜Jšœ˜—J˜Jšœ˜J˜J˜J˜—J˜J™J™Jšœœœ ˜"Jšœœœ ˜#J˜š ž œœœœœ œ˜6šœœœ œ ˜>Jšœœ œœ˜*—šœœ˜%Jšœ(˜(Jšœ˜J˜ J˜J˜—šœœ˜&Jšœ)˜)J˜J˜J˜—Jšœ˜J˜&Jšœ˜J˜—J˜J™Jšœ˜J˜J˜J˜—…—q2£[