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š œ œœœ œœœ ˜