DIRECTORY Basics USING[CompareCard, Comparison], Breakpoint USING[Break, BreakpointProc, ClearBreakpoint, EnumerateBreakpoints, SetBreakpoint, Cant, CantClear, CantSet], BreakWorldArchitecture USING[Address, AddressRep, WorldAccess, WorldAccessFromBreakWorld, BreakWorld, BreakWorldFromBreakWorldAddress, CreateBreakWorld, NewAddress, nullAddress, PatchAreaRep, Cant, WouldBlock], CCTypes USING[CCError, CCErrorCase], CirioBreakAccess USING[], CirioNubAccess USING[FileEntry, GetFileEntry, GetInstructionSetAndOperatingSystem, Handle, LookupFileEntryByStemName, LookupMatchingSymEntryByName, LookupMatchingSymEntryByValue, LookupSymEntryByName, LookupSymEntryByValue, ModuleType, MonitoredCall, RaFromCi, Read32BitsAsCard, Read4Bytes, RemoteAddress, SymEntry, TextType, Write4Bytes], CirioTargets, IO USING[card, PutFR, PutFR1], MIPSArchitecture USING [MIPSAddress, MIPSContents, MIPSAddressFromTargetAddress, MIPSContentsFromTargetContents, TargetContentsFromMIPSContents, NullMIPSAddress], RedBlackTree, Rope USING[Concat, Equal, Length, ROPE, Substr], SourceFileOpsExtras USING [FullFormatPosition, Position], SPARCArchitecture USING[SPARCAddress, SPARCAddressFromTargetAddress, SPARCContents, SPARCContentsFromTargetContents, TargetContentsFromSPARCContents, NullSPARCAddress], SystemInterface USING[ShowReport], TargetArchitecture USING[Contents]; CirioBreakAccessImpl: CEDAR MONITOR LOCKS breaks USING breaks: CirioBreakSet IMPORTS Basics, Breakpoint, BreakWorldArchitecture, CCTypes, CirioNubAccess, IO, MIPSArchitecture, RedBlackTree, Rope, SourceFileOpsExtras, SPARCArchitecture, SystemInterface EXPORTS CirioBreakAccess = BEGIN maxJump: CARD ~ 2**23; CirioBreakSet: TYPE = REF CirioBreakSetBody; CirioBreakSetBody: PUBLIC TYPE = MONITORED RECORD[ nub: CirioNubAccess.Handle, recentBreakIndex: CARD, breakProc: BreakWorldArchitecture.Address, breakProcDataSegment: BreakWorldArchitecture.Address, debuggeeBreakWorld: BreakWorldArchitecture.BreakWorld, afterLastSpacer: CARD, spacers: RedBlackTree.Table--of Spacer-- ]; Spacer: TYPE ~ REF SpacerPrivate; SpacerPrivate: TYPE ~ RECORD [start, size: CARD]; CirioBreakClientData: TYPE = REF CirioBreakClientDataBody; CirioBreakClientDataBody: TYPE = RECORD[ index: CARD, cardAddress: CARD32, stopAll: BOOLEAN, -- added for two kinds of breaks. break: Breakpoint.Break, breakSet: CirioBreakSet, mesaPosition: SourceFileOpsExtras.Position]; QuaBreakSet: PUBLIC PROC [ra: REF ANY] RETURNS [is: BOOL, it: CirioBreakSet] ~ { WITH ra SELECT FROM x: CirioBreakSet => RETURN [TRUE, x]; ENDCASE => RETURN [FALSE, NIL]; }; CreateCirioBreakSet: PUBLIC PROC[nub: CirioNubAccess.Handle, fileNameStem: Rope.ROPE, breakProcName: Rope.ROPE] RETURNS[CirioBreakSet] = BEGIN breakProcAddr, breakProcDataSegment: CARD; breaks: CirioBreakSet; breakWorld: BreakWorldArchitecture.BreakWorld; target: CirioTargets.Target _ NARROW[nub.target]; SPARC: Rope.ROPE _ "SPARC"; RS6000: Rope.ROPE _ "RS6000"; MIPSEL: Rope.ROPE _ "MIPSEL"; -- MIPS little endian MIPSEB: Rope.ROPE _ "MIPSEB"; -- MIPS big endian instrSet, opSys: Rope.ROPE; [instrSet, opSys] _ CirioNubAccess.GetInstructionSetAndOperatingSystem[nub]; [breakProcAddr, breakProcDataSegment] _ FindNamedProcInNamedFile[nub, fileNameStem, breakProcName]; breaks _ NEW[CirioBreakSetBody_ [nub: nub, recentBreakIndex: 0, breakProc: NIL, breakProcDataSegment: NIL, debuggeeBreakWorld: NIL, afterLastSpacer: 0, spacers: RedBlackTree.Create[SpacerGetKey, SpacerCompare] ]]; SELECT TRUE FROM Rope.Equal[instrSet, SPARC], Rope.Equal[instrSet, RS6000] => { breakWorld _ BreakWorldArchitecture.CreateBreakWorld[ name: target.instrSet, peekContents: CirioDebuggeePeekContentsProc, pokeContents: CirioDebuggeePokeContentsProc, getProcAddress: CirioDebuggeeGetProcAddressProc, getProcDataSegment: CirioDebuggeeGetDataSegmentProc, getPatchArea: CirioDebuggeeGetPatchAreaProc, monitoredCall: CirioDebuggeeMonitoredCallProc, worldAccessData: breaks]; }; Rope.Equal[instrSet, MIPSEL], Rope.Equal[instrSet, MIPSEB] => { breakWorld _ BreakWorldArchitecture.CreateBreakWorld[ name: target.instrSet, peekContents: MIPSCirioDebuggeePeekContentsProc, pokeContents: MIPSCirioDebuggeePokeContentsProc, getProcAddress: MIPSCirioDebuggeeGetProcAddressProc, getProcDataSegment: MIPSCirioDebuggeeGetDataSegmentProc, getPatchArea: MIPSCirioDebuggeeGetPatchAreaProc, monitoredCall: MIPSCirioDebuggeeMonitoredCallProc, worldAccessData: breaks]; }; ENDCASE => CCTypes.CCError[cirioError, "Unsupported instrSet for CirioBreakSet"]; breaks.breakProc _ IF breakProcAddr # 0 THEN BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[breakProcAddr]] ELSE BreakWorldArchitecture.nullAddress; breaks.debuggeeBreakWorld _ breakWorld; RETURN[breaks]; END; BreakWorldFromBreakSet: PUBLIC PROC[breaks: CirioBreakSet] RETURNS [BreakWorldArchitecture.BreakWorld] ~ { breakWorld: BreakWorldArchitecture.BreakWorld ~ breaks.debuggeeBreakWorld; RETURN [breakWorld]; }; ReportBreakInfo: PROC[breaks: CirioBreakSet, clientBreak: CirioBreakClientData, clientMessage: Rope.ROPE] = { symEntry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupSymEntryByValue[breaks.nub, clientBreak.cardAddress, 0]; procName: Rope.ROPE _ IF symEntry = NIL THEN CCTypes.CCError[cirioError, "Bad args to LookupAddr"] ELSE symEntry.name; SystemInterface.ShowReport[IO.PutFR[ "%g%g at %g = 0x%08x (abs), stopAll=%g.\n", [rope[clientMessage]], [cardinal[clientBreak.index]], [rope[SourceFileOpsExtras.FullFormatPosition[clientBreak.mesaPosition]]], [cardinal[clientBreak.cardAddress]], [boolean[clientBreak.stopAll]] ], $urgent]; }; ClearBreakAtAbsAddr: PUBLIC ENTRY PROC[breaks: CirioBreakSet, cardAddress: CARD32] = BEGIN ENABLE { UNWIND => NULL; Breakpoint.Cant => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.Cant[%g, %g] when trying to clear break at address %g", [atom[code]], [rope[message]], [cardinal[cardAddress]] ]]; Breakpoint.CantClear => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.CantClear[%g, %g] when trying to clear break at address %g", [atom[code]], [rope[message]], [cardinal[cardAddress]] ]]; }; breakWorld: BreakWorldArchitecture.BreakWorld _ breaks.debuggeeBreakWorld; clientBreak: CirioBreakClientData _ NIL; examineBreaks: Breakpoint.BreakpointProc = BEGIN tentative: CirioBreakClientData _ NARROW[clientData]; IF tentative.cardAddress = cardAddress THEN {clientBreak _ tentative; Breakpoint.ClearBreakpoint[clientBreak.break]; ReportBreakInfo[breaks, clientBreak, "Cleared break #"]; RETURN[TRUE]}; RETURN[FALSE]; END; [] _ Breakpoint.EnumerateBreakpoints[breakWorld, examineBreaks]; IF clientBreak = NIL THEN SystemInterface.ShowReport[IO.PutFR1["sorry, there is no break to clear at %g", IO.card[cardAddress]], $urgent]; END; ClearBreakAtIndex: PUBLIC ENTRY PROC[breaks: CirioBreakSet, index: CARD] = BEGIN ENABLE { UNWIND => NULL; Breakpoint.Cant => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.Cant[%g, %g] when trying to clear break at index %g", [atom[code]], [rope[message]], [cardinal[index]] ]]; Breakpoint.CantClear => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.CantClear[%g, %g] when trying to clear break at index %g", [atom[code]], [rope[message]], [cardinal[index]] ]]; }; breakWorld: BreakWorldArchitecture.BreakWorld _ breaks.debuggeeBreakWorld; clientBreak: CirioBreakClientData _ NIL; examineBreaks: Breakpoint.BreakpointProc = BEGIN tentative: CirioBreakClientData _ NARROW[clientData]; IF tentative.index = index THEN {clientBreak _ tentative; Breakpoint.ClearBreakpoint[clientBreak.break]; ReportBreakInfo[breaks, clientBreak, "Cleared break #"]; RETURN[TRUE]}; RETURN[FALSE]; END; [] _ Breakpoint.EnumerateBreakpoints[breakWorld, examineBreaks]; IF clientBreak = NIL THEN SystemInterface.ShowReport[IO.PutFR1["sorry, there is no break with index %g", IO.card[index]], $urgent]; END; ClearAllBreaks: PUBLIC ENTRY PROC[breaks: CirioBreakSet] = BEGIN ENABLE UNWIND => NULL; breakWorld: BreakWorldArchitecture.BreakWorld _ breaks.debuggeeBreakWorld; examineBreaks: Breakpoint.BreakpointProc = BEGIN clientBreak: CirioBreakClientData _ NARROW[clientData]; Breakpoint.ClearBreakpoint[clientBreak.break ! Breakpoint.CantClear => { SystemInterface.ShowReport[IO.PutFR["Breakpoint.CantClear[%g, %g] when trying to clear break with index %g at %g", [atom[code]], [rope[message]], [cardinal[clientBreak.index]], [cardinal[clientBreak.cardAddress]] ], $normal]; GOTO Givup}; Breakpoint.Cant => { SystemInterface.ShowReport[IO.PutFR["Breakpoint.Cant[%g, %g] when trying to clear break with index %g at %g", [atom[code]], [rope[message]], [cardinal[clientBreak.index]], [cardinal[clientBreak.cardAddress]] ], $normal]; GOTO Givup} ]; ReportBreakInfo[breaks, clientBreak, "Cleared break #"]; RETURN[FALSE]; EXITS Givup => quit _ FALSE END; [] _ Breakpoint.EnumerateBreakpoints[breakWorld, examineBreaks !Breakpoint.Cant => CCTypes.CCError[cirioError, "Can't enumerate breakpoints"]]; END; BreakSetBroken: PUBLIC PROC [breaks: CirioBreakSet] RETURNS [BOOL] ~ { RETURN [breaks.breakProc = BreakWorldArchitecture.nullAddress]}; ListBreaks: PUBLIC ENTRY PROC[breaks: CirioBreakSet] = BEGIN ENABLE UNWIND => NULL; breakWorld: BreakWorldArchitecture.BreakWorld _ breaks.debuggeeBreakWorld; examineBreaks: Breakpoint.BreakpointProc = BEGIN clientBreak: CirioBreakClientData _ NARROW[clientData]; ReportBreakInfo[breaks, clientBreak, "Break #"]; RETURN[FALSE]; END; [] _ Breakpoint.EnumerateBreakpoints[breakWorld, examineBreaks !Breakpoint.Cant => CCTypes.CCError[cirioError, "Can't enumerate breakpoints"]]; END; SetBreakAtAbsAddr: PUBLIC ENTRY PROC[breaks: CirioBreakSet, cardAddress: CARD32, mesaPos: SourceFileOpsExtras.Position, stopAll: BOOLEAN] = BEGIN ENABLE UNWIND => NULL; stopAllDummy: CARD32 _ IF stopAll THEN 1 ELSE 0; breakWorld: BreakWorldArchitecture.BreakWorld _ breaks.debuggeeBreakWorld; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; clientData: CirioBreakClientData _ NEW[CirioBreakClientDataBody_[ breaks.recentBreakIndex _ breaks.recentBreakIndex+1, cardAddress, stopAll, NIL, breaks, mesaPos]]; address: BreakWorldArchitecture.Address _ BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[cardAddress]]; clientData.break _ Breakpoint.SetBreakpoint[address, clientData, breaks.breakProc, stopAllDummy ! Breakpoint.CantSet => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.CantSet[%g, %g] at %g", [atom[code]], [rope[message]], [cardinal[cardAddress]] ]]; Breakpoint.Cant => CCTypes.CCError[cirioError, IO.PutFR["Breakpoint.Cant[%g, %g] when trying to set break at %g", [atom[code]], [rope[message]], [cardinal[cardAddress]] ]] ]; ReportBreakInfo[breaks, clientData, "Set break #"]; END; CirioDebuggeePeekContentsProc: PROC[address: BreakWorldArchitecture.Address] RETURNS [TargetArchitecture.Contents] = BEGIN ENABLE { SPARCArchitecture.NullSPARCAddress => CCTypes.CCError[cirioError, "SPARCArchitecture.NullSPARCAddress"]; BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; }; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address]]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; sparcAddress: SPARCArchitecture.SPARCAddress _ SPARCArchitecture.SPARCAddressFromTargetAddress[address.address]; byteAddress: CARD32 _ LOOPHOLE[sparcAddress]; remoteAddress: CirioNubAccess.RemoteAddress _ [ breaks.nub, byteAddress, 0, FALSE, TRUE]; remoteContents: PACKED ARRAY [0..3] OF BYTE _ CirioNubAccess.Read4Bytes[remoteAddress]; sparcContents: SPARCArchitecture.SPARCContents _ LOOPHOLE[remoteContents]; RETURN[SPARCArchitecture.TargetContentsFromSPARCContents[sparcContents]]; END; MIPSCirioDebuggeePeekContentsProc: PROC[address: BreakWorldArchitecture.Address] RETURNS [TargetArchitecture.Contents] = BEGIN ENABLE { MIPSArchitecture.NullMIPSAddress => CCTypes.CCError[cirioError, "MIPSArchitecture.NullMIPSAddress"]; BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; }; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address]]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; mipsAddress: MIPSArchitecture.MIPSAddress _ MIPSArchitecture.MIPSAddressFromTargetAddress[address.address]; byteAddress: CARD32 _ LOOPHOLE[mipsAddress]; remoteAddress: CirioNubAccess.RemoteAddress _ [ breaks.nub, byteAddress, 0, FALSE, TRUE]; remoteContents: PACKED ARRAY [0..3] OF BYTE _ LOOPHOLE[CirioNubAccess.Read4Bytes[remoteAddress]]; mipsContents: MIPSArchitecture.MIPSContents _ LOOPHOLE[remoteContents]; RETURN[MIPSArchitecture.TargetContentsFromMIPSContents[mipsContents]]; END; CirioDebuggeePokeContentsProc: PROC[address: BreakWorldArchitecture.Address, contents: TargetArchitecture.Contents] = BEGIN ENABLE { SPARCArchitecture.NullSPARCAddress => CCTypes.CCError[cirioError, "SPARCArchitecture.NullSPARCAddress"]; BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; }; breakWorld: BreakWorldArchitecture.BreakWorld _ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address]; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; sparcAddress: SPARCArchitecture.SPARCAddress _ SPARCArchitecture.SPARCAddressFromTargetAddress[address.address]; byteAddress: CARD32 _ LOOPHOLE[sparcAddress]; remoteAddress: CirioNubAccess.RemoteAddress _ [ breaks.nub, byteAddress, 0, FALSE, TRUE]; sparcContents: SPARCArchitecture.SPARCContents _ SPARCArchitecture.SPARCContentsFromTargetContents[contents]; remoteContents: PACKED ARRAY [0..3] OF BYTE _ LOOPHOLE[sparcContents]; CirioNubAccess.Write4Bytes[remoteAddress, remoteContents]; END; MIPSCirioDebuggeePokeContentsProc: PROC[address: BreakWorldArchitecture.Address, contents: TargetArchitecture.Contents] = BEGIN ENABLE { MIPSArchitecture.NullMIPSAddress => CCTypes.CCError[cirioError, "MIPSArchitecture.NullMIPSAddress"]; BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; }; breakWorld: BreakWorldArchitecture.BreakWorld _ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address]; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; mipsAddress: MIPSArchitecture.MIPSAddress _ MIPSArchitecture.MIPSAddressFromTargetAddress[address.address]; byteAddress: CARD32 _ LOOPHOLE[mipsAddress]; remoteAddress: CirioNubAccess.RemoteAddress _ [ breaks.nub, byteAddress, 0, FALSE, TRUE]; mipsContents: MIPSArchitecture.MIPSContents _ MIPSArchitecture.MIPSContentsFromTargetContents[contents]; remoteContents: PACKED ARRAY [0..3] OF BYTE _ LOOPHOLE[mipsContents]; CirioNubAccess.Write4Bytes[remoteAddress, remoteContents]; END; CirioDebuggeeGetProcAddressProc: PROC[breakWorld: BreakWorldArchitecture.BreakWorld, procName: Rope.ROPE] RETURNS [BreakWorldArchitecture.Address] = BEGIN ENABLE BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; symEntry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupSymEntryByName[breaks.nub, procName, FALSE, FALSE, 0]; IF symEntry = NIL THEN CCTypes.CCError[cirioError, ""] ELSE RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[symEntry.value]]]; END; MIPSCirioDebuggeeGetProcAddressProc: PROC[breakWorld: BreakWorldArchitecture.BreakWorld, procName: Rope.ROPE] RETURNS [BreakWorldArchitecture.Address] = BEGIN ENABLE BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; symEntry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupSymEntryByName[breaks.nub, procName, FALSE, FALSE, 0]; IF symEntry = NIL THEN CCTypes.CCError[cirioError, ""] ELSE RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[symEntry.value]]]; END; CirioDebuggeeGetDataSegmentProc: PROC[breakWorld: BreakWorldArchitecture.BreakWorld, address: BreakWorldArchitecture.Address] RETURNS [BreakWorldArchitecture.Address] = BEGIN ENABLE BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; sparcAddress: SPARCArchitecture.SPARCAddress _ SPARCArchitecture.SPARCAddressFromTargetAddress[address.address]; symEntry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupSymEntryByValue[breaks.nub, LOOPHOLE[sparcAddress], 0]; fileEntry: CirioNubAccess.FileEntry; tocName: Rope.ROPE; tocEntry: CirioNubAccess.SymEntry; tocAddress: CARD; IF symEntry = NIL THEN CCTypes.CCError[cirioError, ""]; fileEntry _ CirioNubAccess.GetFileEntry[breaks.nub, symEntry.fileSeqNum]; IF fileEntry = NIL THEN CCTypes.CCError[cirioError, ""]; tocName _ Rope.Substr[symEntry.name, 1, Rope.Length[symEntry.name] - 1]; tocEntry _ CirioNubAccess.LookupSymEntryByName[breaks.nub, tocName, FALSE, FALSE, 0]; IF tocEntry = NIL THEN RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[fileEntry.dataReloc]]]; tocAddress _ CirioNubAccess.RaFromCi[breaks.nub, tocEntry.value, 32].Read32BitsAsCard; RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[tocAddress]]]; END; MIPSCirioDebuggeeGetDataSegmentProc: PROC[breakWorld: BreakWorldArchitecture.BreakWorld, address: BreakWorldArchitecture.Address] RETURNS [BreakWorldArchitecture.Address] = BEGIN ENABLE BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; mipsAddress: MIPSArchitecture.MIPSAddress _ MIPSArchitecture.MIPSAddressFromTargetAddress[address.address]; symEntry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupSymEntryByValue[breaks.nub, LOOPHOLE[mipsAddress], 0]; fileEntry: CirioNubAccess.FileEntry; tocName: Rope.ROPE; tocEntry: CirioNubAccess.SymEntry; tocAddress: CARD; IF symEntry = NIL THEN CCTypes.CCError[cirioError, ""]; fileEntry _ CirioNubAccess.GetFileEntry[breaks.nub, symEntry.fileSeqNum]; IF fileEntry = NIL THEN CCTypes.CCError[cirioError, ""]; tocName _ Rope.Substr[symEntry.name, 1, Rope.Length[symEntry.name] - 1]; tocEntry _ CirioNubAccess.LookupSymEntryByName[breaks.nub, tocName, FALSE, FALSE, 0]; IF tocEntry = NIL THEN RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[fileEntry.dataReloc]]]; tocAddress _ CirioNubAccess.RaFromCi[breaks.nub, tocEntry.value, 32].Read32BitsAsCard; RETURN[BreakWorldArchitecture.NewAddress[breakWorld, LOOPHOLE[tocAddress]]]; END; CirioDebuggeeGetPatchAreaProc: PROC[address: BreakWorldArchitecture.Address] RETURNS [BreakWorldArchitecture.PatchAreaRep] = { ENABLE { SPARCArchitecture.NullSPARCAddress => CCTypes.CCError[cirioError, "SPARCArchitecture.NullSPARCAddress"]; BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; BreakWorldArchitecture.WouldBlock => CCTypes.CCError[cirioError, IO.PutFR1["BreakWorldArchitecture.WouldBlock[%g]", [rope[message]] ]]; }; breakWorld: BreakWorldArchitecture.BreakWorld _ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address]; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; sparcAddress: SPARCArchitecture.SPARCAddress _ SPARCArchitecture.SPARCAddressFromTargetAddress[address.address]; byteAddress: CARD32 _ LOOPHOLE[sparcAddress]; symEntry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupSymEntryByValue[breaks.nub, byteAddress, 0]; patchAddress: BreakWorldArchitecture.Address _ NIL; patchAreaRep: BreakWorldArchitecture.PatchAreaRep _ [NIL, 0]; key: Spacer; TrySpacers: PROC RETURNS [{no, maybe, yes}] ~ { raLeft, raAt, raRight: REF ANY; spLeft, spAt, spRight: Spacer; leftDist, rightDist: CARD _ CARD.LAST; [raLeft, raAt, raRight] _ breaks.spacers.Lookup3[key]; spLeft _ NARROW[raLeft]; spAt _ NARROW[raAt]; spRight _ NARROW[raRight]; SELECT TRUE FROM spAt#NIL, spLeft#NIL AND CARD[byteAddress-spLeft.start] < spLeft.size => CCTypes.CCError[cirioError, IO.PutFR1["trying to set breakpoint in a meadow! (at address 0x%x)", [cardinal[byteAddress]] ]]; spLeft=NIL AND spRight=NIL => RETURN[maybe]; ENDCASE => { IF spLeft#NIL THEN leftDist _ byteAddress-spLeft.start; IF spRight#NIL THEN rightDist _ spRight.start+spRight.size - byteAddress; SELECT TRUE FROM leftDist<=rightDist AND NearEnough[byteAddress, spLeft.start]=>{ patchAddress _ NEW[BreakWorldArchitecture.AddressRep_[breakWorld, LOOPHOLE[spLeft.start]]]; patchAreaRep _ [patchAddress, spLeft.size]; RETURN[yes]}; rightDist<=leftDist AND NearEnough[byteAddress, spRight.start+spRight.size] => { patchAddress _ NEW[BreakWorldArchitecture.AddressRep_[breakWorld, LOOPHOLE[spRight.start]]]; patchAreaRep _ [patchAddress, spRight.size]; RETURN[yes]}; leftDist=CARD.LAST OR rightDist=CARD.LAST => RETURN [maybe]; ENDCASE => RETURN [no]}; }; IF symEntry # NIL THEN { fileEntry: CirioNubAccess.FileEntry _ CirioNubAccess.GetFileEntry[breaks.nub, symEntry.fileSeqNum]; IF fileEntry.patchSize>0 THEN { patchAddress _ NEW[BreakWorldArchitecture.AddressRep_[breakWorld, LOOPHOLE[fileEntry.patchReloc]]]; patchAreaRep _ [patchAddress, fileEntry.patchSize]; RETURN[patchAreaRep]}}; key _ NEW[SpacerPrivate _ [byteAddress, 0]]; SELECT TrySpacers[] FROM yes => RETURN[patchAreaRep]; maybe => { UpdateSpacers[breaks]; IF TrySpacers[]=yes THEN RETURN[patchAreaRep]; }; no => NULL; ENDCASE => ERROR; IF symEntry # NIL THEN { lo, hi: CARD _ symEntry.fileSeqNum; gohi: BOOL _ TRUE; fileEntry: CirioNubAccess.FileEntry; WHILE gohi OR lo>1 DO IF lo>1 THEN { lo _ lo - 1; fileEntry _ CirioNubAccess.GetFileEntry[breaks.nub, lo]; SELECT TRUE FROM fileEntry.patchSize=0 => NULL; NearEnough[fileEntry.patchReloc, byteAddress] => { patchAddress _ NEW[BreakWorldArchitecture.AddressRep_[breakWorld, LOOPHOLE[fileEntry.patchReloc]]]; patchAreaRep _ [patchAddress, fileEntry.patchSize]; RETURN[patchAreaRep]}; ENDCASE => lo _ 0; }; IF gohi THEN { hi _ hi + 1; fileEntry _ CirioNubAccess.GetFileEntry[breaks.nub, hi]; SELECT TRUE FROM fileEntry.seqNum < hi => gohi _ FALSE; fileEntry.patchSize=0 => NULL; NearEnough[fileEntry.patchReloc+fileEntry.patchSize, byteAddress] => { patchAddress _ NEW[BreakWorldArchitecture.AddressRep_[breakWorld, LOOPHOLE[fileEntry.patchReloc]]]; patchAreaRep _ [patchAddress, fileEntry.patchSize]; RETURN[patchAreaRep]}; ENDCASE => gohi _ FALSE; }; ENDLOOP; }; CCTypes.CCError[cirioError, IO.PutFR1["no SymEntry or nearby patch space for address 0x%x", [cardinal[byteAddress]] ]] }; MIPSCirioDebuggeeGetPatchAreaProc: PROC[address: BreakWorldArchitecture.Address] RETURNS [BreakWorldArchitecture.PatchAreaRep] = { ENABLE { MIPSArchitecture.NullMIPSAddress => CCTypes.CCError[cirioError, "MIPSArchitecture.NullMIPSAddress"]; BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; BreakWorldArchitecture.WouldBlock => CCTypes.CCError[cirioError, IO.PutFR1["BreakWorldArchitecture.WouldBlock[%g]", [rope[message]] ]]; }; breakWorld: BreakWorldArchitecture.BreakWorld _ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address]; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; mipsAddress: MIPSArchitecture.MIPSAddress _ MIPSArchitecture.MIPSAddressFromTargetAddress[address.address]; byteAddress: CARD32 _ LOOPHOLE[mipsAddress]; symEntry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupSymEntryByValue[breaks.nub, byteAddress, 0]; patchAddress: BreakWorldArchitecture.Address _ NIL; patchAreaRep: BreakWorldArchitecture.PatchAreaRep _ [NIL, 0]; key: Spacer; TrySpacers: PROC RETURNS [{no, maybe, yes}] ~ { raLeft, raAt, raRight: REF ANY; spLeft, spAt, spRight: Spacer; leftDist, rightDist: CARD _ CARD.LAST; [raLeft, raAt, raRight] _ breaks.spacers.Lookup3[key]; spLeft _ NARROW[raLeft]; spAt _ NARROW[raAt]; spRight _ NARROW[raRight]; SELECT TRUE FROM spAt#NIL, spLeft#NIL AND CARD[byteAddress-spLeft.start] < spLeft.size => CCTypes.CCError[cirioError, IO.PutFR1["trying to set breakpoint in a meadow! (at address 0x%x)", [cardinal[byteAddress]] ]]; spLeft=NIL AND spRight=NIL => RETURN[maybe]; ENDCASE => { IF spLeft#NIL THEN leftDist _ byteAddress-spLeft.start; IF spRight#NIL THEN rightDist _ spRight.start+spRight.size - byteAddress; SELECT TRUE FROM leftDist<=rightDist AND NearEnough[byteAddress, spLeft.start]=>{ patchAddress _ NEW[BreakWorldArchitecture.AddressRep_[breakWorld, LOOPHOLE[spLeft.start]]]; patchAreaRep _ [patchAddress, spLeft.size]; RETURN[yes]}; rightDist<=leftDist AND NearEnough[byteAddress, spRight.start+spRight.size] => { patchAddress _ NEW[BreakWorldArchitecture.AddressRep_[breakWorld, LOOPHOLE[spRight.start]]]; patchAreaRep _ [patchAddress, spRight.size]; RETURN[yes]}; leftDist=CARD.LAST OR rightDist=CARD.LAST => RETURN [maybe]; ENDCASE => RETURN [no]}; }; IF symEntry # NIL THEN { fileEntry: CirioNubAccess.FileEntry _ CirioNubAccess.GetFileEntry[breaks.nub, symEntry.fileSeqNum]; IF fileEntry.patchSize>0 THEN { patchAddress _ NEW[BreakWorldArchitecture.AddressRep_[breakWorld, LOOPHOLE[fileEntry.patchReloc]]]; patchAreaRep _ [patchAddress, fileEntry.patchSize]; RETURN[patchAreaRep]}}; key _ NEW[SpacerPrivate _ [byteAddress, 0]]; SELECT TrySpacers[] FROM yes => RETURN[patchAreaRep]; maybe => { UpdateSpacers[breaks]; IF TrySpacers[]=yes THEN RETURN[patchAreaRep]; }; no => NULL; ENDCASE => ERROR; IF symEntry # NIL THEN { lo, hi: CARD _ symEntry.fileSeqNum; gohi: BOOL _ TRUE; fileEntry: CirioNubAccess.FileEntry; WHILE gohi OR lo>1 DO IF lo>1 THEN { lo _ lo - 1; fileEntry _ CirioNubAccess.GetFileEntry[breaks.nub, lo]; SELECT TRUE FROM fileEntry.patchSize=0 => NULL; NearEnough[fileEntry.patchReloc, byteAddress] => { patchAddress _ NEW[BreakWorldArchitecture.AddressRep_[breakWorld, LOOPHOLE[fileEntry.patchReloc]]]; patchAreaRep _ [patchAddress, fileEntry.patchSize]; RETURN[patchAreaRep]}; ENDCASE => lo _ 0; }; IF gohi THEN { hi _ hi + 1; fileEntry _ CirioNubAccess.GetFileEntry[breaks.nub, hi]; SELECT TRUE FROM fileEntry.seqNum < hi => gohi _ FALSE; fileEntry.patchSize=0 => NULL; NearEnough[fileEntry.patchReloc+fileEntry.patchSize, byteAddress] => { patchAddress _ NEW[BreakWorldArchitecture.AddressRep_[breakWorld, LOOPHOLE[fileEntry.patchReloc]]]; patchAreaRep _ [patchAddress, fileEntry.patchSize]; RETURN[patchAreaRep]}; ENDCASE => gohi _ FALSE; }; ENDLOOP; }; CCTypes.CCError[cirioError, IO.PutFR1["no SymEntry or nearby patch space for address 0x%x", [cardinal[byteAddress]] ]] }; NearEnough: PROC [a, b: CARD] RETURNS [BOOL] ~ { SELECT TRUE FROM a=b => RETURN [TRUE]; a RETURN [b-a < 2**21]; a>b => RETURN [a-b < 2**21]; ENDCASE => ERROR}; UpdateSpacers: PROC [breaks: CirioBreakSet] ~ { se: CirioNubAccess.SymEntry; target: CirioTargets.Target _ NARROW[breaks.nub.target]; highest: CARD _ 0; some: BOOL _ FALSE; FOR se _ CirioNubAccess.LookupSymEntryByName[h: breaks.nub, sym: target.CNameToLoaderName[target, "SomePatchSpace"], caseSensitive: FALSE, externOnly: FALSE, numToSkip: 0], CirioNubAccess.LookupMatchingSymEntryByName[h: breaks.nub, symID: se.symID, pattern: target.CNameToLoaderName[target, "SomePatchSpace"], caseSensitive: FALSE, wantedTypes: CirioNubAccess.TextType, ignoreClasses: 2--externals--, numToSkip: -1] WHILE se#NIL AND se.value >= breaks.afterLastSpacer DO s: Spacer ~ NEW [SpacerPrivate _ [se.value, spacerSize]]; breaks.spacers.Insert[s, s]; highest _ MAX[highest, se.value]; some _ TRUE; ENDLOOP; IF some THEN breaks.afterLastSpacer _ MAX[breaks.afterLastSpacer, highest+spacerSize]; RETURN}; spacerSize: CARD ~ 8192--that's how much the source reserves--; SpacerGetKey: PROC [data: REF ANY] RETURNS [REF ANY] --RedBlackTree.GetKey-- ~ {RETURN[data]}; SpacerCompare: PROC [k, data: REF ANY] RETURNS [Basics.Comparison] --RedBlackTree.Compare-- ~ { s1: Spacer ~ NARROW[k]; s2: Spacer ~ NARROW[data]; RETURN Basics.CompareCard[s1.start, s2.start]}; CirioDebuggeeMonitoredCallProc: PROC[address: BreakWorldArchitecture.Address, proc: PROCEDURE [] RETURNS []] = BEGIN ENABLE { SPARCArchitecture.NullSPARCAddress => CCTypes.CCError[cirioError, "SPARCArchitecture.NullSPARCAddress"]; BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; BreakWorldArchitecture.WouldBlock => CCTypes.CCError[cirioError, IO.PutFR1["BreakWorldArchitecture.WouldBlock[%g]", [rope[message]] ]]; }; breakWorld: BreakWorldArchitecture.BreakWorld _ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address]; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; sparcAddress: SPARCArchitecture.SPARCAddress _ SPARCArchitecture.SPARCAddressFromTargetAddress[address.address]; byteAddress: CARD32 _ LOOPHOLE[sparcAddress]; remoteAddress: CirioNubAccess.RemoteAddress _ [ breaks.nub, byteAddress, 0, FALSE, TRUE]; CirioNubAccess.MonitoredCall[remoteAddress, proc]; END; MIPSCirioDebuggeeMonitoredCallProc: PROC[address: BreakWorldArchitecture.Address, proc: PROCEDURE [] RETURNS []] = BEGIN ENABLE { MIPSArchitecture.NullMIPSAddress => CCTypes.CCError[cirioError, "MIPSArchitecture.NullMIPSAddress"]; BreakWorldArchitecture.Cant => CCTypes.CCError[cirioError, IO.PutFR["BreakWorldArchitecture.Cant[%g, %g]", [atom[code]], [rope[message]] ]]; BreakWorldArchitecture.WouldBlock => CCTypes.CCError[cirioError, IO.PutFR1["BreakWorldArchitecture.WouldBlock[%g]", [rope[message]] ]]; }; breakWorld: BreakWorldArchitecture.BreakWorld _ BreakWorldArchitecture.BreakWorldFromBreakWorldAddress[address]; worldAccess: BreakWorldArchitecture.WorldAccess _ BreakWorldArchitecture.WorldAccessFromBreakWorld[breakWorld]; breaks: CirioBreakSet _ NARROW[worldAccess.data]; mipsAddress: MIPSArchitecture.MIPSAddress _ MIPSArchitecture.MIPSAddressFromTargetAddress[address.address]; byteAddress: CARD32 _ LOOPHOLE[mipsAddress]; remoteAddress: CirioNubAccess.RemoteAddress _ [ breaks.nub, byteAddress, 0, FALSE, TRUE]; CirioNubAccess.MonitoredCall[remoteAddress, proc]; END; ModuleType: CARD = CirioNubAccess.ModuleType; TextType: CARD = CirioNubAccess.TextType; FindNamedProcInNamedFile: PROC[nub: CirioNubAccess.Handle, fileStemName: Rope.ROPE, procName: Rope.ROPE] RETURNS[CARD, CARD] = BEGIN target: CirioTargets.Target _ NARROW[nub.target]; loaderName: Rope.ROPE _ target.CNameToLoaderName[target, procName]; lookUpName: Rope.ROPE _ Rope.Concat[loaderName, "_P"]; lookUpNameSize: CARD _ Rope.Length[lookUpName]; moduleEntry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupFileEntryByStemName[nub, fileStemName, 0]; fileEntry: CirioNubAccess.FileEntry; tocName: Rope.ROPE; tocEntry: CirioNubAccess.SymEntry; tocAddress: CARD; IF moduleEntry # NIL THEN BEGIN previousEntry: CirioNubAccess.SymEntry _ moduleEntry; fullName: Rope.ROPE _ Rope.Concat[loaderName, "_P60"]; entry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupMatchingSymEntryByName[nub, 0, fullName, FALSE, TextType, 0, 0]; IF entry # NIL THEN RETURN[entry.value, moduleEntry.value]; DO nextEntry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupMatchingSymEntryByValue[nub, previousEntry.symID, moduleEntry.value, TextType, 0, 1]; IF nextEntry = NIL THEN RETURN[0, 0]; -- no such proc in the file IF nextEntry.value >= moduleEntry.value + moduleEntry.size THEN RETURN[0, 0]; -- no such proc in the file IF Rope.Equal[Rope.Substr[nextEntry.name, 0, lookUpNameSize], lookUpName] THEN { fileEntry _ CirioNubAccess.GetFileEntry[nub, moduleEntry.fileSeqNum]; IF fileEntry = NIL THEN RETURN [0, 0]; tocName _ Rope.Substr[nextEntry.name, 1, Rope.Length[nextEntry.name] - 1]; tocEntry _ CirioNubAccess.LookupSymEntryByName[nub, tocName, FALSE, FALSE, 0]; IF tocEntry = NIL THEN RETURN[nextEntry.value, fileEntry.dataReloc]; tocAddress _ CirioNubAccess.RaFromCi[nub, tocEntry.value, 32].Read32BitsAsCard; RETURN[nextEntry.value, tocAddress]; }; previousEntry _ nextEntry; ENDLOOP; END ELSE -- we couldnt find the file, so look up the symbol blindly (for compatibility) BEGIN fullName: Rope.ROPE _ Rope.Concat[loaderName, "_P60"]; IF NOT Rope.Equal[procName, "CallDebugger"] THEN RETURN[0, 0] ELSE BEGIN entry: CirioNubAccess.SymEntry _ CirioNubAccess.LookupMatchingSymEntryByName[nub, 0, fullName, FALSE, TextType, 0, 0]; IF entry = NIL THEN RETURN[0, 0]; -- no such proc fileEntry _ CirioNubAccess.GetFileEntry[nub, entry.fileSeqNum]; IF fileEntry = NIL THEN RETURN [0, 0]; tocName _ Rope.Substr[entry.name, 1, Rope.Length[entry.name] - 1]; tocEntry _ CirioNubAccess.LookupSymEntryByName[nub, tocName, FALSE, FALSE, 0]; IF tocEntry = NIL THEN RETURN[entry.value, fileEntry.dataReloc]; tocAddress _ CirioNubAccess.RaFromCi[nub, tocEntry.value, 32].Read32BitsAsCard; RETURN[entry.value, tocAddress]; END; END; END; END. ¼ CirioBreakAccessImpl.mesa Copyright Ó 1991, 1992, 1993 by Xerox Corporation. All rights reserved. Sturgis, March 23, 1990 11:59 am PST Last changed by Theimer on October 31, 1989 5:35:37 pm PST Peter B. Kessler, July 30, 1990 12:13 pm PDT Spreitze, July 24, 1992 7:36 pm PDT Philip James, December 26, 1991 3:49 pm PST Udagawa, February 15, 1991 4:09 pm PST Laurie Horton, August 15, 1992 10:16 am PDT Katsuyuki Komatsu January 6, 1993 11:41 am PST Jas, November 19, 1992 9:32 am PST Breakpoints A CirioBreakSet (or the debuggeeBreakWorld??) also plays the role of a debuggee for Peter see [Palain-NFS]peter>CirioBreaks>FakeCirioImpl.mesa for a sample implementation breakProcName was _CallDebugger_P60 instruction set names note: debuggeeData.breakProc is invalid, however, it won't be needed until after we fill it in below. (Because breaks is not yet publically available.) PROCEDURE [clientData: ClientData] RETURNS [quit: BOOLEAN] IF clientBreak # NIL THEN BEGIN Breakpoint.ClearBreakpoint[clientBreak.break]; SystemInterface.ShowReport[IO.PutFR["break with index %g cleared at %g", IO.card[clientBreak.index], IO.card[clientBreak.cardAddress]]]; END ELSE SystemInterface.ShowReport[IO.PutFR["sorry, there is no break to clear at %g", IO.card[cardAddress]]]; PROCEDURE [clientData: ClientData] RETURNS [quit: BOOLEAN] IF clientBreak # NIL THEN BEGIN Breakpoint.ClearBreakpoint[clientBreak.break]; SystemInterface.ShowReport[IO.PutFR["break with index %g cleared at %g", IO.card[clientBreak.index], IO.card[clientBreak.cardAddress]]]; END ELSE SystemInterface.ShowReport[IO.PutFR1["sorry, there is no break with index %g", IO.card[index]]]; PROCEDURE [clientData: ClientData] RETURNS [quit: BOOLEAN] SystemInterface.ShowReport[IO.PutFR["break with index %g cleared at %g", IO.card[clientBreak.index], IO.card[clientBreak.cardAddress]]]; PROCEDURE [clientData: ClientData] RETURNS [quit: BOOLEAN] These two constants come from /jaune/xrhome/DEVELOPMENT/INCLUDE/xr/IncrementalLoad.h don't forget that the bottom bit should be ignored, as it is the "external" bit. This procedure should be in LoadStateAccess!!! The procedure name is as it would appear in mesa NOTE: as soon as target worlds get the version 6 nub, then we should always find an appropriate fileEntry. So, we should change the logic of this code. Blindly look for it first, this is for the interm MIPS version, jas. End of the interm stuff, jas. Important remark: my understanding of LookupMatchingSymEntryByValue is that it ignores moduleEntry.value if previousEntry.symID # 0, hence we progress through successively higher valued entries, all greater or equal to the start of the file. we are looking for an entry with a name of the form "_ProcName_Pxx" �Ê4•NewlineDelimiter ™codešœ™K™HKšœ$™$K™:K™,K™#K™+K™&K™+K™.K™"—K˜šÏk ˜ Kšœ�œ˜&Kšœ �œh˜xKšœ�œ¶˜ÒKšœ�œ˜$Kšœ�œ˜Kšœ�œ¿˜ÓKšœ ˜ Kš�œ�œ˜Kšœ�œŒ˜¢K˜ Kšœ�œ�œ ˜0Kšœ�œ ˜9Kšœ�œ‘˜¨Kšœ�œ ˜"Kšœ�œ ˜#—K˜šÏnœ�œ�˜#Kš�œ�œ˜(Kš�œF�œ`˜¯Kš�œ˜—Kšœ�˜K˜K˜K˜K™™ K˜Kšœ �œ ˜K˜Kšœ�œ�œ˜,š œ�œ�œ� œ�œ˜2Kšœ˜Kšœ�œ˜Kšœ*˜*Kšœ5˜5Kšœ6˜6Kšœ�œ˜KšœÏc ˜(šœ˜K™šœY™YKšœW™W———K˜Kšœ�œ�œ˜!Kšœ�œ�œ�œ˜1K˜Kšœ�œ�œ˜:šœ�œ�œ˜(Kšœ�œ˜ Kšœ �œ˜Kšœ �œŸ!˜3Kšœ˜Kšœ˜Kšœ,˜,—K˜šž œ�œ�œ�œ�œ�œ�œ˜Pš�œ�œ�˜Kšœ�œ�œ˜%Kš�œ�œ�œ�œ˜—K˜—˜Kšœ#™#—š žœ�œ�œ0�œ�œ�œ˜ˆKš�˜Kšœ%�œ˜*Kšœ˜Kšœ.˜.Kšœ�œ ˜1K˜™Kš�œ�œ ˜Kš�œ�œ ˜Kš�œ�œŸ˜7Kš�œ�œŸ˜4—K˜Kšœ�œ˜KšœL˜LK˜Kšœc˜cšœ �œ˜Kšœ ˜ Kšœ˜Kšœ �œ˜Kšœ�œ˜Kšœ�œ˜Kšœ˜Kšœ=˜=—K˜š�œ�œ�˜Kšœ�œ˜šœ�œ˜ K˜šœ ˜ šœ(˜(Kšœ˜Kšœ,˜,Kšœ,˜,Kšœ0˜0Kšœ4˜4Kšœ,˜,Kšœ.˜.šœ˜Kšœ˜™˜———Kšœ˜—Kšœ�œ˜šœ�œ˜ K˜šœ ˜ šœ(˜(Kšœ˜Kšœ0˜0Kšœ0˜0Kšœ4˜4Kšœ8˜8Kšœ0˜0Kšœ2˜2Kšœ˜——Kšœ˜—Kš�œJ˜QK˜—Kš œ�œ�œ/�œ�œ$˜œK˜Kšœ'˜'K˜Kš�œ ˜Kš�œ˜—K˜šžœ�œ�œ�œ(˜kKšœJ˜JK˜Kš�œ˜K˜—K˜šžœ�œO�œ˜mKšœq˜qKš œ�œ�œ �œ�œ7�œ˜všœ�œ˜$Kšœ+˜+Kšœ˜Kšœ˜KšœI˜IKšœ$˜$Kšœ+˜+—Kšœ˜K˜—š žœ�œ�œ�œ%�œ˜TKš�˜š�œ˜Kš�œ�œ˜Kšœ/�œ…˜¶Kšœ4�œŠ˜ÀKšœ˜—KšœJ˜JKšœ$�œ˜(šœ*˜*Kš� œ�œ�œ™:Kš�˜Kšœ"�œ ˜5š�œ%�˜+Kšœ‚�œ�œ˜�—Kš�œ�œ˜Kš�œ˜—Kšœ@˜@Kš �œ�œ�œ�œ3�œ˜ŠK˜š�œ�œ�™Kš�™Kšœ.™.Kšœ�œ,�œ�œ!™ˆKš�™—šœ�™Kšœ�œ2�œ™f—Kš�œ˜—K˜š žœ�œ�œ�œ�œ˜JKš�˜š�œ˜Kš�œ�œ˜Kšœ/�œ}˜®Kšœ4�œ‚˜¸Kšœ˜—KšœJ˜JKšœ$�œ˜(šœ*˜*Kš� œ�œ�œ™:Kš�˜Kšœ"�œ ˜5š�œ�˜Kšœ‚�œ�œ˜�—Kš�œ�œ˜Kš�œ˜—Kšœ@˜@Kš �œ�œ�œ�œ2�œ˜ƒK˜š�œ�œ�™Kš�™Kšœ.™.Kšœ�œ,�œ�œ!™ˆKš�™—šœ�™Kšœ�œ2�œ™`—Kš�œ˜—K˜šžœ�œ�œ�œ˜:Kš�˜Kš�œ�œ�œ˜KšœJ˜Jšœ*˜*Kš� œ�œ�œ™:Kš�˜Kšœ$�œ ˜7šœ.˜.šœ˜Kšœ�œÄ˜áKš�œ˜ —šœ˜Kšœ�œ¿˜ÜKš�œ˜ —K˜—Kšœ�œ,�œ�œ!™ˆKšœ8˜8Kš�œ�œ˜Kš�œ�˜Kš�œ˜—Kšœ�˜�Kš�œ˜K˜—š žœ�œ�œ�œ�œ˜FKš�œ:˜@K˜—šž œ�œ�œ�œ˜6Kš�˜Kš�œ�œ�œ˜KšœJ˜Jšœ*˜*Kš� œ�œ�œ™:Kš�˜Kšœ$�œ ˜7Kšœ0˜0Kš�œ�œ˜Kš�œ˜—šœ>˜>KšœP˜P—Kš�œ˜—K˜š žœ�œ�œ�œ%�œ2�œ˜‹Kš�˜Kš�œ�œ�œ˜Kš œ�œ�œ �œ�œ˜0KšœJ˜JKšœo˜ošœ#�œ˜AKšœ4˜4Kšœ ˜ Kšœ˜Kš�œ˜K˜K˜ —KšœX�œ˜ošœa˜aKšœ2�œe˜™Kšœ/�œz˜«Kšœ˜—Kšœ3˜3Kš�œ˜—K™K˜šžœ�œ*�œ ˜tš�œ�œ˜Kšœh˜hKšœ;�œO˜ŒKšœ˜—Kšœ¥˜¥Kšœ�œ˜1Kšœp˜pKšœ �œ�œ˜-šœ/˜/Kšœ ˜ Kšœ ˜ K˜Kš�œ˜Kš�œ˜—Kš œ�œ�œ�œ�œ,˜WKšœ1�œ˜JKš�œC˜IKš�œ˜K˜—šž!œ�œ*�œ ˜xš�œ�œ˜Kšœd˜dKšœ;�œO˜ŒKšœ˜—Kšœ¥˜¥Kšœ�œ˜1Kšœk˜kKšœ �œ�œ˜,šœ/˜/Kšœ ˜ Kšœ ˜ K˜Kš�œ˜Kš�œ˜—Kš œ�œ�œ�œ�œ�œ+˜aKšœ.�œ˜GKš�œ@˜FKš�œ˜K˜—šžœ�œR˜uš�œ�œ˜Kšœh˜hKšœ;�œO˜ŒKšœ˜—Kšœp˜pKšœp˜pKšœ�œ˜1Kšœp˜pKšœ �œ�œ˜-šœ/˜/Kšœ ˜ Kšœ ˜ K˜Kš�œ˜Kš�œ˜—Kšœm˜mKš œ�œ�œ�œ�œ�œ˜FKšœ:˜:Kš�œ˜K˜—Kšž!œ�œR˜yš�œ�œ˜Kšœd˜dKšœ;�œO˜ŒKšœ˜—Kšœp˜pKšœp˜pKšœ�œ˜1Kšœk˜kKšœ �œ�œ˜,šœ/˜/Kšœ ˜ Kšœ ˜ K˜Kš�œ˜Kš�œ˜—Kšœh˜hKš œ�œ�œ�œ�œ�œ˜EKšœ:˜:Kš�œ˜K˜šžœ�œ?�œ�œ#˜”Kš�œ�œ<�œO˜™Kšœp˜pKšœ�œ˜1Kšœ^�œ�œ˜oK˜Kš �œ �œ�œ!�œ�œ/�œ˜ŒKš�œ˜K˜—šž#œ�œ?�œ�œ#˜˜Kš�œ�œ<�œO˜™Kšœp˜pKšœ�œ˜1Kšœ^�œ�œ˜oK˜Kš �œ �œ�œ!�œ�œ/�œ˜ŒKš�œ˜—K˜šžœ�œY�œ#˜¨Kš�œ�œ<�œO˜™Kšœp˜pKšœ�œ˜1Kšœp˜pKšœU�œ˜pK˜$Kšœ�œ˜Kšœ"˜"Kšœ �œ˜K˜Kš�œ �œ�œ!˜7KšœI˜IKš�œ �œ�œ!˜8KšœH˜HKšœD�œ�œ˜UKš �œ �œ�œ�œ/�œ˜lKšœV˜VKš�œ/�œ˜LKš�œ˜K˜—šž#œ�œY�œ#˜¬Kš�œ�œ<�œO˜™Kšœp˜pKšœ�œ˜1Kšœk˜kKšœU�œ˜oK˜$Kšœ�œ˜Kšœ"˜"Kšœ �œ˜K˜Kš�œ �œ�œ!˜7KšœI˜IKš�œ �œ�œ!˜8KšœH˜HKšœD�œ�œ˜UKš �œ �œ�œ�œ/�œ˜lKšœV˜VKš�œ/�œ˜LKš�œ˜K˜—šžœ�œ*�œ*˜~š�œ˜Kšœh˜hKšœ;�œO˜ŒKšœA�œD˜‡Kšœ˜—Kšœp˜pKšœp˜pKšœ�œ˜1Kšœp˜pKšœ �œ�œ˜-Kšœe˜eKšœ/�œ˜3Kšœ5�œ˜=Kšœ ˜ šž œ�œ�œ˜/Kšœ�œ�œ˜K˜Kšœ�œ�œ�œ˜&Kšœ6˜6Kšœ �œ ˜Kšœ�œ˜Kšœ �œ ˜š�œ�œ�˜Kš œ�œ �œ�œ�œH�œ^˜ÅKš œ�œ�œ �œ�œ˜,š�œ˜ Kš�œ�œ�œ%˜7Kš�œ �œ�œ6˜Iš�œ�œ�˜šœ�œ)˜@Kšœ�œ0�œ˜[Kšœ+˜+Kš�œ˜ —šœ�œ9˜PKšœ�œ0�œ˜\Kšœ,˜,Kš�œ˜ —Kš œ �œ�œ�œ �œ�œ�œ ˜