DIRECTORY Basics USING [BITSHIFT, LowHalf], Convert USING [CardFromRope, Error], FS USING [Error, StreamOpen], IntelHex, IO, LarkPrograms, List USING [Remove], Rope USING [Compare, Concat, Equal, Fetch, Find, Length, ROPE, Substr], TeleLoad; LarkProgramsImpl: CEDAR PROGRAM IMPORTS Basics, Convert, FS, IntelHex, IO, List, Rope, TeleLoad EXPORTS LarkPrograms = { programs: LIST OF REF ANY; nullItem: PUBLIC LarkPrograms.STObject _ ["???", 0, procedure]; monitor: LarkPrograms.Program; slaveMonitor: LarkPrograms.Program; Junk: SIGNAL = CODE; ReadProgramFromMBFile: PUBLIC PROC [mbFileName: Rope.ROPE, baseAddress: TeleLoad.CoreAddress, log: IO.STREAM _ NIL, addressSpace: TeleLoad.AddressSpace _ main] RETURNS [p: LarkPrograms.Program] = TRUSTED { trueFileName: Rope.ROPE _ mbFileName; mbFileStream: IO.STREAM; data: PACKED ARRAY [0..TeleLoad.MaxByte) OF TeleLoad.Byte; count: NAT _ 0; nameLength: NAT; IWord: PROC RETURNS [x: CARDINAL] = TRUSTED { x _ LOOPHOLE[mbFileStream.GetChar[], CARDINAL]; x _ Basics.BITSHIFT[x, 8]; x _ x + LOOPHOLE[mbFileStream.GetChar[], CARDINAL]; RETURN[x]; }; IByte: PROC RETURNS [x: CARDINAL] = TRUSTED { x _ LOOPHOLE[mbFileStream.GetChar[], CARDINAL]; RETURN[x]; }; DataFlush: PROC = TRUSTED { cb: TeleLoad.CoreBlock; cb _ NEW[TeleLoad.CoreBlockObject[count]]; cb.address _ baseAddress; FOR i: CARDINAL IN [0..count) DO cb.data[i] _ data[i]; ENDLOOP; baseAddress _ baseAddress + count; count _ 0; p.program _ CONS[cb, p.program]; IF log # NIL THEN log.PutRope["! "]; }; p _ NEW[LarkPrograms.ProgramObject]; p.addressSpace _ addressSpace; IF trueFileName = NIL THEN { IF log # NIL THEN log.PutF["Bad file name\n"]; RETURN[NIL]; }; IF log # NIL THEN log.PutF[" ParseMB %g: ", IO.rope[trueFileName]]; IF Rope.Find[s1: trueFileName, s2: ".", pos1: 0, case: FALSE] = -1 THEN trueFileName _ Rope.Concat[trueFileName, ".mb"]; p.programName _ trueFileName; mbFileStream _ FS.StreamOpen[fileName: trueFileName, wDir: "///" ! FS.Error => TRUSTED { IF log # NIL THEN log.PutRope[" ... file not found\n"]; CONTINUE; }]; IF mbFileStream = NIL THEN RETURN[NIL]; { IF IWord[] # 4 THEN GOTO BadFormat; -- boilerplate IF IWord[] # 1 THEN GOTO BadFormat; -- memory number IF IWord[] # 8 THEN GOTO BadFormat; -- 8 bit memory nameLength _ 0; DO nameLength _ nameLength + 1; IF IByte[] = 0 THEN EXIT; ENDLOOP; IF nameLength MOD 2 # 0 THEN [] _ IByte[]; -- skip to even boundary IF IWord[] # 2 THEN GOTO BadFormat; -- set current memory and location IF IWord[] # 1 THEN GOTO BadFormat; -- memory number IF IWord[] # 0 THEN GOTO BadFormat; -- 8 bit memory DO SELECT IWord[] FROM 0 => EXIT; 1 => NULL; -- memory data; ENDCASE => GOTO BadFormat; IF IWord[] # 0 THEN GOTO BadFormat; -- source line number (not used) data[count] _ IByte[]; [] _ IByte[]; count _ count + 1; IF count >= TeleLoad.MaxByte THEN DataFlush[]; ENDLOOP; DataFlush[]; FOR i: TeleLoad.Registers8086 IN [AX..FL] DO p.startState.Regs[i] _ 0; ENDLOOP; p.startState.Regs[SP] _ 54256; -- D3F0 p.startState.Regs[IP] _ Basics.LowHalf[baseAddress]; IF log # NIL THEN log.PutRope["\n"]; EXITS BadFormat => { IF log # NIL THEN log.PutRope[" Bad MB file format\n"]; p _ NIL; }; }; mbFileStream.Close[]; }; ReadProgramFromDisk: PUBLIC PROC [objectFileName: Rope.ROPE, log: IO.STREAM _ NIL, addressSpace: TeleLoad.AddressSpace _ main] RETURNS [p: LarkPrograms.Program] = TRUSTED { trueFileName: Rope.ROPE _ objectFileName; myEndOf: IntelHex.EndOfProc = TRUSTED { RETURN[objectFileStream.EndOf[]]; }; myGetByte: IntelHex.GetByteProc = TRUSTED { RETURN[objectFileStream.GetChar[] - 0C]; }; myGetPosition: IntelHex.GetPositionProc = TRUSTED { RETURN[LOOPHOLE[objectFileStream.GetIndex[], LONG CARDINAL]]; }; mySetPosition: IntelHex.SetPositionProc = TRUSTED { objectFileStream.SetIndex[LOOPHOLE[p, INT]]; }; myPutC: IntelHex.PutCharProc = TRUSTED { IF log # NIL THEN log.PutChar[c]; }; myPutEndRecord: IntelHex.PutEndRecordProc = TRUSTED { DataFlush[]; IF log # NIL THEN log.PutF[" ...End"]; }; myPutStartRecord: IntelHex.PutStartRecordProc = TRUSTED { DataFlush[]; IF log # NIL THEN log.PutF[" ...Start at %04x:%04x", IO.int[frame], IO.int[bytepos]]; p.startState[CS] _ frame; p.startState[IP] _ bytepos; }; myDataByte: IntelHex.DataByteProc = TRUSTED { IF gnaflag OR adr#gda THEN DataFlush[]; gdata[gptr] _ d; IF gptr = 0 THEN fda _ adr; gptr _ gptr+1; gda _ adr+1; IF gptr >= TeleLoad.locMaxByte THEN DataFlush[]; }; DataFlush: PROC = TRUSTED { cb: TeleLoad.CoreBlock; IF gptr=0 THEN RETURN; gnaflag _ FALSE; cb _ NEW[TeleLoad.CoreBlockObject[gptr]]; cb.address _ fda; FOR i: CARDINAL IN [0..gptr) DO cb.data[i] _ gdata[i]; ENDLOOP; gptr _ 0; p.program _ CONS[cb, p.program]; IF log # NIL THEN log.PutRope["! "]; }; gda: LONG CARDINAL _ LAST[LONG CARDINAL]; fda: LONG CARDINAL; gptr: CARDINAL _ 0; gdata: PACKED ARRAY [0..TeleLoad.MaxByte) OF TeleLoad.Byte; gnaflag: BOOLEAN _ TRUE; objectFileStream: IO.STREAM; { myIStream: IntelHex.IStream _ [ endof: myEndOf, get: myGetByte, GetPosition: myGetPosition, SetPosition: mySetPosition ]; myOStream: IntelHex.OStream _ [ PutEndRecord: myPutEndRecord, PutStartRecord: myPutStartRecord, DataByte: myDataByte ]; p _ NEW[LarkPrograms.ProgramObject]; p.addressSpace _ addressSpace; IF trueFileName = NIL THEN { IF log # NIL THEN log.PutF["Bad file name\n"]; RETURN[NIL]; }; IF log # NIL THEN log.PutF[" Parse %g: ", IO.rope[trueFileName]]; IF Rope.Find[s1: trueFileName, s2: ".", pos1: 0, case: FALSE] = -1 THEN trueFileName _ Rope.Concat[trueFileName, ".obj"]; p.programName _ trueFileName; objectFileStream _ FS.StreamOpen[fileName: trueFileName, wDir: "///" ! FS.Error => TRUSTED { IF log # NIL THEN log.PutRope[" ... file not found\n"]; CONTINUE; }]; IF objectFileStream = NIL THEN RETURN[NIL]; FOR i: TeleLoad.Registers8086 IN [AX..FL] DO p.startState.Regs[i] _ 0; ENDLOOP; p.startState.Regs[SP] _ 54256; -- D3F0 IF NOT IntelHex.ProcessFile[in: myIStream, out: myOStream, errs: myPutC] THEN { IF log # NIL THEN log.PutRope[" ...IntelHex Failure"]; p _ NIL; } ELSE { extension: INT _ Rope.Find[s1: p.programName, s2: ".obj", case: FALSE]; patchFileName: Rope.ROPE; IF extension # -1 THEN BuildSearchTable[program: p, name: Rope.Substr[base: p.programName, start: 0, len: extension], log: log]; IF p.searchTable # NIL AND log # NIL THEN log.PutF[" ... symbol table read"]; ResetPatchList[p]; patchFileName _ Rope.Concat[Rope.Substr[base: p.programName, start: 0, len: extension], ".patches"]; p.patchList _ PatchFromFile[program: p, fileName: patchFileName, log: log]; }; IF log # NIL THEN log.PutRope["\n"]; objectFileStream.Close[]; }; }; ResetPatchList: PUBLIC PROC [program: LarkPrograms.Program] = { IF program#NIL THEN program.patchList _ NIL; }; PatchProgramWord: PUBLIC PROC [program: LarkPrograms.Program, address: TeleLoad.CoreAddress, wordValue: CARDINAL, name: Rope.ROPE _ NIL] = { item: LarkPrograms.PatchItem _ NEW[LarkPrograms.PatchItemObject _ [name: name, address: address, wordValue: wordValue]]; program.patchList _ CONS[item, program.patchList]; }; Monitor: PUBLIC PROC [log: IO.STREAM _ NIL, addressSpace: TeleLoad.AddressSpace _ main] RETURNS [mon: LarkPrograms.Program] = { IF addressSpace = main THEN { IF monitor # NIL THEN RETURN[monitor]; monitor _ ReadProgramFromDisk[objectFileName: "LarkMon", log: log, addressSpace: main]; IF monitor.nameTable = NIL OR monitor.searchTable = NIL THEN monitor _ NIL; mon _ monitor; } ELSE { IF slaveMonitor # NIL THEN RETURN[slaveMonitor]; slaveMonitor _ ReadProgramFromMBFile[mbFileName: "LarkSlave.mb", baseAddress: 0E000H, log: log, addressSpace: slave]; BuildSearchTable[program: slaveMonitor, name: "LarkSlave", log: log]; mon _ slaveMonitor; }; }; GetHex: PROC [h: IO.STREAM] RETURNS [n: LONG CARDINAL] = { r: Rope.ROPE _ h.GetTokenRope[].token; n _ Convert.CardFromRope[r, 16 ! Convert.Error => SIGNAL Junk]; }; BuildSearchTable: PUBLIC PROC [program: LarkPrograms.Program, name: Rope.ROPE, log: IO.STREAM _ NIL] = {{ tabFile: IO.STREAM; count: INT; type: Rope.ROPE; program.searchTable _ NIL; program.nameTable _ NIL; tabFile _ FS.StreamOpen[fileName: name, wDir: "///" ! FS.Error => TRUSTED { tabFile _ NIL; CONTINUE; }]; IF tabFile = NIL THEN tabFile _ FS.StreamOpen[fileName: Rope.Concat[name, ".syms"], wDir: "///" ! FS.Error => TRUSTED { tabFile _ NIL; IF log # NIL THEN log.PutF["Cannot find %g or %g\n", IO.rope[name], IO.rope[Rope.Concat[name, ".syms"]]]; GOTO Die; }]; type _ tabFile.GetTokenRope[].token; IF NOT Rope.Equal[type, "Names", FALSE] THEN GOTO BadFormat; count _ tabFile.GetInt[]; program.nameTable _NEW[LarkPrograms.SymTabObject[count]]; FOR i: INT IN [0..count) DO program.nameTable[i].type _ IF Rope.Equal[tabFile.GetTokenRope[].token, "V"] THEN variable ELSE procedure; program.nameTable[i].addr _ GetHex[tabFile]; program.nameTable[i].id _ tabFile.GetTokenRope[].token; ENDLOOP; BubbleSortNameTable[program.nameTable]; type _ tabFile.GetTokenRope[].token; IF NOT Rope.Equal[type, "Addresses", FALSE] THEN GOTO BadFormat; count _ tabFile.GetInt[]; program.searchTable _ NEW[LarkPrograms.SymTabObject[count]]; FOR i: INT IN [0..count) DO program.searchTable[i].type _ IF Rope.Equal[tabFile.GetTokenRope[].token, "V"] THEN variable ELSE procedure; program.searchTable[i].addr _ GetHex[tabFile]; program.searchTable[i].id _ tabFile.GetTokenRope[].token; ENDLOOP; BubbleSortSearchTable[program.searchTable]; tabFile.Close[]; EXITS BadFormat => { IF log # NIL THEN log.PutF["Bad symbols format\n"]; program.searchTable _ NIL; program.nameTable _ NIL; NULL; }; Die => NULL; };}; BubbleSortSearchTable: PROC [searchTable: LarkPrograms.SymbolTable] = { anyswap: BOOL _ TRUE; temp: LarkPrograms.STObject; WHILE anyswap DO anyswap _ FALSE; FOR i: NAT IN [0..searchTable.length-1) DO IF searchTable[i].addr > searchTable[i+1].addr THEN { temp _ searchTable[i]; searchTable[i] _ searchTable[i+1]; searchTable[i+1] _ temp; anyswap _ TRUE; }; ENDLOOP; ENDLOOP; }; BubbleSortNameTable: PROC [nameTable: LarkPrograms.SymbolTable] = { anyswap: BOOL _ TRUE; temp: LarkPrograms.STObject; WHILE anyswap DO anyswap _ FALSE; FOR i: NAT IN [0..nameTable.length-1) DO IF Rope.Compare[nameTable[i].id, nameTable[i+1].id, FALSE] = greater THEN { temp _ nameTable[i]; nameTable[i] _ nameTable[i+1]; nameTable[i+1] _ temp; anyswap _ TRUE; }; ENDLOOP; ENDLOOP; }; ProcedureEnclosing: PUBLIC PROC [program: LarkPrograms.Program, addr: TeleLoad.CoreAddress, searchMonitor: BOOL _ TRUE] RETURNS [s: LarkPrograms.STObject] = { u: NAT; l: NAT _ 0; i: NAT; query: INT = addr; IF program = NIL OR program.searchTable = NIL THEN { IF searchMonitor THEN RETURN[ProcedureEnclosing[program: Monitor[], addr: addr, searchMonitor: FALSE]] ELSE RETURN[nullItem]; }; u _ program.searchTable.length - 1; DO i _ (l+u)/2; IF u < l THEN ERROR; IF program.searchTable[i].addr <= query THEN { IF i = (program.searchTable.length-1) THEN { IF searchMonitor THEN { s _ ProcedureEnclosing[program: Monitor[], addr: addr, searchMonitor: FALSE]; IF s # nullItem THEN RETURN[s]; }; RETURN [program.searchTable[i]]; }; IF program.searchTable[i+1].addr > query THEN RETURN [program.searchTable[i]] ELSE l _ i + 1; } ELSE { IF i = 0 THEN RETURN [nullItem] ELSE u _ i - 1; }; ENDLOOP; }; FindVariable: PUBLIC PROC [program: LarkPrograms.Program, name: Rope.ROPE, searchMonitor: BOOL _ TRUE] RETURNS [item: LarkPrograms.STObject, found: BOOL] = {{ u: NAT; l: NAT _ 0; i: NAT; IF program = NIL OR program.nameTable = NIL THEN GOTO TryMonitor; u _ program.nameTable.length - 1; DO i _ (l+u)/2; IF u < l THEN GOTO TryMonitor; SELECT Rope.Compare[name, program.nameTable[i].id, FALSE] FROM less => { IF i = 0 THEN GOTO TryMonitor ELSE u _ i - 1; }; greater => { IF i = (program.nameTable.length-1) THEN GOTO TryMonitor ELSE l _ i + 1; }; equal => RETURN [item: program.nameTable[i], found: TRUE]; ENDCASE => ERROR; ENDLOOP; EXITS TryMonitor => { IF searchMonitor THEN { [item: item, found: found] _ FindVariable[program: Monitor[], name: name, searchMonitor: FALSE]; RETURN[item: item, found: found]; } ELSE { IF IsNumber[name] THEN { item.id _ "???"; item.addr _ Convert.CardFromRope[name, 16 ! Convert.Error => GOTO GiveUp]; item.type _ variable; RETURN[item: item, found: TRUE]; } ELSE RETURN[item: nullItem, found: FALSE]; }; }; }; EXITS GiveUp => RETURN[item: nullItem, found: FALSE]; }; IsNumber: PROC [r: Rope.ROPE] RETURNS [BOOL] = { FOR i: INT IN [0..r.Length[]) DO SELECT r.Fetch[i] FROM IN ['0..'9], IN ['a..'f], IN ['A..'F] => LOOP; ENDCASE => RETURN [FALSE]; ENDLOOP; RETURN[TRUE]; }; EnumeratePrograms: PUBLIC PROC [proc: PROC [program: LarkPrograms.Program] RETURNS [stop: BOOL]] = { list: LIST OF REF ANY _ programs; WHILE list # NIL DO IF proc[NARROW[list.first, LarkPrograms.Program]] THEN EXIT; list _ list.rest; ENDLOOP; }; FetchProgram: PUBLIC PROC [programName: Rope.ROPE] RETURNS [p: LarkPrograms.Program] = TRUSTED { searchName: Rope.ROPE _ programName; lmap: PROC [item: REF ANY] = TRUSTED { lp: LarkPrograms.Program _ NARROW[item]; IF Rope.Equal[lp.programName, searchName, FALSE] THEN p _ lp; }; IF Rope.Find[s1: searchName, s2: ".", pos1: 0, case: FALSE] = -1 THEN searchName _ Rope.Concat[searchName, ".obj"]; MyMap[list: programs, proc: lmap]; }; AddOrReplaceProgram: PUBLIC PROC [program: LarkPrograms.Program] = { old: LarkPrograms.Program _ FetchProgram[program.programName]; IF old # NIL THEN programs _ List.Remove[ref: old, list: programs]; programs _ CONS[program, programs]; IF monitor # NIL AND Rope.Equal[program.programName, monitor.programName, FALSE] THEN monitor _ program; }; MyMap: PROC [list: LIST OF REF ANY, proc: PROC [item: REF ANY]] = { WHILE list # NIL DO proc[list.first]; list _ list.rest; ENDLOOP; }; PatchFromFile: PUBLIC PROC [program: LarkPrograms.Program, fileName: Rope.ROPE, log: IO.STREAM _ NIL] RETURNS [LIST OF LarkPrograms.PatchItem] = { id: Rope.ROPE; value: CARDINAL; offset: NAT _ 0; patFile: IO.STREAM; line: Rope.ROPE; lis: IO.STREAM; pl: LIST OF LarkPrograms.PatchItem _ NIL; item: LarkPrograms.STObject; found: BOOL; { IF log # NIL THEN log.PutF["Patches from file %g\n", IO.rope[fileName]]; patFile _ FS.StreamOpen[fileName: fileName, wDir: "///" ! FS.Error => TRUSTED { patFile _ NIL; GOTO GiveUp; }]; IF patFile = NIL THEN { IF log # NIL THEN log.PutF["Cannot find %g\n", IO.rope[fileName]]; RETURN[NIL]; }; DO { ENABLE { IO.Error => { IF log # NIL THEN log.PutRope["IO.Error!\n"]; pl _ NIL; GOTO CloseFile; }; IO.EndOfStream => GOTO CloseFile; }; IF patFile.EndOf[] THEN GOTO CloseFile; line _ IO.GetLineRope[patFile]; IF line.Length <= 1 THEN GOTO CloseFile; IF line.Fetch[0] = '- THEN { IF log # NIL THEN log.PutF["%g\n", IO.rope[line]]; GOTO TryNextLine; }; lis _ IO.RIS[rope: line, oldStream: lis]; id _ lis.GetTokenRope[].token; offset _ GetHex[lis]; value _ GetHex[lis]; [item: item, found: found] _ FindVariable[program, id]; IF NOT found THEN { IF log # NIL THEN log.PutF[" %g: not found\n", IO.rope[id]]; RETURN [NIL]; } ELSE pl _ CONS[NEW[LarkPrograms.PatchItemObject _ [name: IF offset = 0 THEN id ELSE IO.PutFR["%g+%xH", IO.rope[id], IO.card[offset]], address: item.addr + offset, wordValue: value]], pl]; EXITS TryNextLine => NULL; }; ENDLOOP; EXITS CloseFile => patFile.Close[]; GiveUp => NULL; }; RETURN [pl]; }; }. April 25, 1983 12:20 pm, LCS, created from old LarkPrograms August 10, 1983 12:02 pm, LCS, bug in PatchProgramWord August 10, 1983 12:02 pm, LCS, added ReadProgramFromMBFile August 30, 1983 3:16 pm, LCS, added slaveMonitor ŽLarkProgramsImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Stewart, December 21, 1983 3:51 pm Last Edited by: Pier, May 30, 1984 12:36:27 pm PDT Swinehart, December 17, 1986 5:14:30 pm PST global variables Now read in the MB file! Write an end record. Write a start record. Autonmatically incorporate patches from a standard place GetHex: PROC [h: IO.Handle] RETURNS [LONG CARDINAL] = { r: Rope.ROPE _ h.GetToken[]; v: Convert.Value _ Convert.Parse[text: [rope[r]], template: [unsigned[base: 16]]].value; RETURN[NARROW[v, Convert.Value[unsigned]! ANY => SIGNAL Junk].unsigned]; }; Swinehart, December 17, 1986 5:13:10 pm PST Force root directory lookups. That's not as good as making LarkControl do the right thing about working directories, but it's better than having lookups be random. changes to: ReadProgramFromMBFile, ReadProgramFromDisk, BuildSearchTable, PatchFromFile Κy˜šœ™Icodešœ Οmœ1™<—Jšœ"™"™2K™+—J˜šΟk ˜ Jšœžœžœ ˜!Jšœžœ˜$Jšžœžœ˜J˜ Jšžœ˜J˜ Jšœžœ ˜Jšœžœ/žœ ˜GJ˜ J˜—Jšœžœž˜Jšžœžœ žœ˜?šžœ˜J˜J˜—Jšœ™Jš œ žœžœžœžœ˜Jšœ žœ0˜@Jšœ˜Jšœ#˜#J˜Jšœžœžœ˜J˜šΟnœžœžœžœ*žœžœžœ.žœžœ˜ΝJšœžœ˜%Jšœžœžœ˜Jšœžœžœžœ˜:Jšœžœ˜Jšœ žœ˜J˜š Ÿœžœžœžœžœ˜-Jšœžœžœ˜/Jšœ žœ˜Jšœžœžœ˜3Jšžœ˜ J˜—š Ÿœžœžœžœžœ˜.Jšœžœžœ˜/Jšžœ˜ J˜—šŸ œžœžœ˜J˜Jšœžœ"˜*Jšœ˜šžœžœžœ ž˜ J˜Jšžœ˜—Jšœ"˜"J˜ Jšœ žœ˜ Jšžœžœžœ˜$˜J˜——Jšœžœ˜$Jšœ˜šžœžœžœ˜Jšžœžœžœ˜.Jšžœžœ˜ J˜—Jšžœžœžœžœ˜FJšžœ5žœžœ1˜xJ˜šœžœ2žœ žœ˜XJšžœžœžœ&˜7Jšžœ˜ J˜—Jš žœžœžœžœžœ˜'J™J˜Jšžœ žœžœ Οc˜3Jšžœ žœžœ  ˜5Jšžœ žœžœ  ˜4J˜šž˜J˜Jšžœ žœžœ˜Jšžœ˜—Jšžœ žœžœ ˜DJšžœ žœžœ  "˜GJšžœ žœžœ  ˜5Jšžœ žœžœ  ˜4šž˜šžœ ž˜Jšœžœ˜ Jšœžœ ˜Jšžœžœ ˜—Jšžœ žœžœ   ˜EJ˜J˜ J˜Jšžœžœ ˜.Jšžœ˜—J˜ Jš žœžœžœžœžœžœ˜OJšœžœ  ˜&Jšœžœ ˜4Jšžœžœžœ˜$Jšž˜˜Jšžœžœžœ&˜7Jšœžœ˜J˜—J˜Jšœ˜J˜J˜—šŸœžœžœžœžœžœžœ.žœžœ˜¬Jšœžœ˜)J˜Jšœžœžœ˜LJ˜Jšœ"žœžœ%˜WJ˜šœ*žœ˜3Jšžœžœžœžœ˜=J˜J˜—Jšœ*žœžœžœ˜cJ˜Jš œžœžœžœžœ˜MJ˜Jšœ™šœ,žœ˜5J˜ Jšžœžœžœ˜&J˜J˜—Jšœ™šœ0žœ˜9J˜ Jš žœžœžœ$žœ žœ˜UJšœ žœ ˜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šœžœ˜Jšœžœžœžœ˜;Jšœ žœžœ˜Jšœžœžœ˜J˜˜J˜J˜J˜J˜J˜J˜—˜J˜J˜!J˜J˜J˜—Jšœžœ˜$Jšœ˜šžœžœžœ˜Jšžœžœžœ˜.Jšžœžœ˜ J˜—Jšžœžœžœžœ˜DJšžœ5žœžœ2˜yJ˜šœžœ2žœ žœ˜\Jšžœžœžœ&˜7Jšžœ˜ J˜—Jš žœžœžœžœžœ˜+Jš žœžœžœžœžœžœ˜OJšœžœ  ˜&šžœžœCžœ˜OJšžœžœžœ%˜6Jšœžœ˜Jšœ˜—šžœ˜Jšœ žœ2žœ˜GJšœžœ˜Jšžœžœj˜€Jš žœžœžœžœžœ$˜MJ™8Jšœ˜Jšœd˜dJšœK˜KJ˜—Jšžœžœžœ˜$J˜J˜J˜J˜—šŸœžœžœ$˜?Jšžœ žœžœžœ˜,J˜J˜—š ŸœžœžœKžœ žœžœ˜ŒJšœžœV˜xJšœžœ˜2J˜J˜—šŸœžœžœžœžœžœ.žœ ˜šžœžœ˜Jšžœ žœžœžœ ˜&JšœW˜WJš žœžœžœžœžœ žœ˜KJšœ˜J˜—šžœ˜Jšžœžœžœžœ˜0Jšœu˜uJšœE˜EJšœ˜J˜—J˜—J˜šŸœžœžœžœžœžœžœ˜:Jšœžœ˜&Jšœ2žœ˜?J˜—š Ÿœžœžœ žœžœžœ™7Jšœžœ™J™XJšžœžœžœžœ™HJ™—J˜šŸœžœžœ,žœžœžœžœ˜iJšœ žœžœ˜Jšœžœ˜ Jšœ žœ˜Jšœžœ˜Jšœžœ˜šœ žœ*žœ žœ˜KJšœ žœ˜Jšžœ˜ J˜—š žœ žœžœ žœ@žœ žœ˜wJšœ žœ˜Jš žœžœžœ$žœ žœ#˜iJšžœ˜ J˜—J˜$Jš žœžœžœžœžœ ˜˜ Jšžœžœžœ ˜Jšžœ ˜J˜—˜ Jšžœ"žœžœ ˜8Jšžœ ˜J˜—Jšœ žœ%žœ˜:Jšžœžœ˜—Jšžœ˜—Jšž˜šœ˜šžœžœ˜JšœYžœ˜`Jšžœ˜!Jšœ˜—šžœ˜šžœžœ˜J˜Jšœ=žœ ˜JJ˜Jšžœžœ˜ J˜—Jšžœžœžœ˜*J˜—J˜—J˜Jšž˜Jšœ žœžœ˜/J˜—J˜š Ÿœžœ žœžœžœ˜0šžœžœžœž˜ šžœ ž˜Jšžœ žœ žœ žœ˜.Jšžœžœžœ˜—Jšžœ˜—Jšžœžœ˜J˜—J˜š Ÿœžœžœžœ!žœžœ˜dJš œžœžœžœžœ ˜!šžœžœž˜Jšžœžœ$žœžœ˜˜>Jšžœžœžœ2˜CJšœ žœ˜#Jš žœ žœžœ6žœžœ˜hJ˜J˜—šŸœžœžœžœžœžœžœžœžœ˜Cšžœžœž˜J˜J˜Jšžœ˜—J˜—J˜šŸ œžœžœ0žœžœžœžœžœžœžœ˜’Jšœ žœ˜Jšœžœ˜Jšœžœ˜Jšœ žœžœ˜Jšœ žœ˜Jšœžœžœ˜Jšœžœžœžœ˜)Jšœ˜Jšœžœ˜ J˜Jšžœžœžœ$žœ˜Hšœ žœ.žœ žœ˜OJšœ žœ˜Jšžœ˜ J˜—šžœ žœžœ˜Jšžœžœžœžœ˜BJšžœžœ˜ J˜—šžœ˜šžœ˜šžœ ˜ Jšžœžœžœ˜-Jšœžœ˜ Jšžœ ˜J˜—Jšžœžœ ˜!J˜—Jšžœžœžœ ˜'Jšœžœ˜Jšžœžœžœ ˜(šžœžœ˜Jšžœžœžœžœ ˜2Jšžœ ˜J˜—Jšœžœžœ˜)J˜J˜J˜Jšœ7˜7šžœžœžœ˜Jšžœžœžœžœ ˜=Jšžœžœ˜ J˜—Jšžœžœžœ'žœ žœžœžœžœ žœE˜»Jšž˜Jšœžœ˜J˜Jšžœ˜—Jšž˜J˜Jšœ žœ˜J˜Jšžœ˜ J˜—J˜J˜Jšœžœ˜;Jšœžœ˜6Jšœžœ˜:Jšœžœ˜0™+K™€Kšœ ΟrK™W—K™—…—