DIRECTORY BasicTime USING [GetClockPulses, Now, Pulses, PulsesToSeconds], Buttons USING [Button, Create, ReLabel, SetDisplayStyle], Containers USING [ChildXBound, ChildYBound, Create], Convert USING [Base, CardFromDecimalLiteral, CardFromHexLiteral, CardFromOctalLiteral, Error, IntFromRope], DragOpsCross USING [Byte, FourBytes, IFUStatusRec, Inst, OnesWord, ProcessorRegister, Word, ZerosWord], DragOpsCrossUtils USING [AddDelta, BytePCToWordAddress, IntToWord, WordToCard, WordToInt], HandCodingSupport USING [Area, GetCurrentArea], HandCodingUtil USING [GetInstArray, GetRegArray, NameArray, RegNameArray, ToStream], InputFocus USING [], IO USING [Close, EndOfStream, Error, GetCedarTokenRope, GetIndex, int, PutF, PutFR, PutRope, RIS, RopeFromROS, rope, ROS, STREAM, TokenKind], IOUtils USING [CopyPFProcs, PFCodeProc, PFProcs, SetPFCodeProc, SetPFProcs], Labels USING [Create, Label, Set], LizardCache USING [CacheBase, FlushCache, NewBase, NewCache, SharedBase], LizardHeart USING [ChangeLogger, ChangeLoggerRep, Control, InstDoneProc, InstructionExecute, InstStartProc, IOChangeProc, MemChangeProc, NewProcessor, OutsideEnvelope, Processor, ProcessorStats, RegChangeProc], LizardToolOutput, Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc], MessageWindow USING [Append, Blink], NumberLabels USING [CreateNumber, NumberLabel, NumberLabelUpdate], Process USING [Detach, MsecToTicks, priorityBackground, SetPriority, SetTimeout], Real USING [RoundLI], Rope USING [Equal, Fetch, ROPE], SparseMemory USING [Base, Fetch], TypeScript USING [ChangeLooks, Create], VFonts USING [], ViewerClasses USING [Viewer], ViewerIO USING [CreateViewerStreams], ViewerOps USING [CreateViewer, OpenIcon], ViewerTools USING [GetContents, GetSelectionContents, SetContents], VTables USING [Create, Install, SetRowsAndColumns, SetTableEntry, VTable], WriteSparseMemory USING [TestAbort, ToStream]; LizardToolDriver: CEDAR MONITOR LOCKS data USING data: ToolData IMPORTS BasicTime, Buttons, Containers, Convert, DragOpsCrossUtils, HandCodingSupport, IO, IOUtils, Labels, LizardCache, LizardHeart, LizardToolOutput, HandCodingUtil, Menus, MessageWindow, NumberLabels, Process, Real, Rope, SparseMemory, TypeScript, ViewerIO, ViewerOps, ViewerTools, VTables, WriteSparseMemory = BEGIN Area: TYPE = HandCodingSupport.Area; Base: TYPE = SparseMemory.Base; Button: TYPE = Buttons.Button; Byte: TYPE = DragOpsCross.Byte; ChangeLogger: TYPE = LizardHeart.ChangeLogger; ChangeLoggerRep: TYPE = LizardHeart.ChangeLoggerRep; Control: TYPE = LizardHeart.Control; Inst: TYPE = DragOpsCross.Inst; Label: TYPE = Labels.Label; NumberLabel: TYPE = NumberLabels.NumberLabel; Processor: TYPE = LizardHeart.Processor; ProcessorRegister: TYPE = DragOpsCross.ProcessorRegister; ProcessorStats: TYPE = LizardHeart.ProcessorStats; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; TestAbort: TYPE = WriteSparseMemory.TestAbort; Viewer: TYPE = ViewerClasses.Viewer; VTable: TYPE = VTables.VTable; Word: TYPE = DragOpsCross.Word; ZerosWord: Word = DragOpsCross.ZerosWord; OnesWord: Word = DragOpsCross.OnesWord; ToolData: TYPE = REF ToolDataRep; ToolDataRep: TYPE = MONITORED RECORD [ refreshMillis: CARDINAL _ 1000, processors: ProcessorSequence _ NIL, area: Area _ NIL, logger: ChangeLogger _ NIL, logTS: Viewer _ NIL, log: STREAM _ NIL, traceButton, baseButton: Buttons.Button _ NIL, vTab: VTable _ NIL, pauseCondText: Viewer _ NIL, pauseCondTable: PauseCondTable _ NIL, statusLabel: Label _ NIL, refresh: CONDITION, controlChange: CONDITION, internalControl: InternalControl _ pause, initPulses: BasicTime.Pulses _ BasicTime.GetClockPulses[], dumpStart: INT _ 0, dumpWords: INT _ LAST[INT], live: BOOL _ FALSE, requestReset, requestReschedule: BOOL _ FALSE, requestDump: DumpRequest _ none, requestBiasStats: BOOL _ FALSE, stopPrint: BOOL _ FALSE, tracing: BOOL _ FALSE, useHex: BOOL _ TRUE, -- will be inverted at setup time to Octal instTable: InstCountTable _ NIL, lastInstTable: LastInstTable _ NIL, newStats, baseStats: ToolStats _ [], biasStats: LizardToolOutput.StatSnapshot ]; ToolStats: TYPE = RECORD [ samplePulses: BasicTime.Pulses _ 0, pStats: ProcessorStats _ [] ]; ProcessorSequence: TYPE = REF ProcessorSequenceRep; ProcessorSequenceRep: TYPE = RECORD [ SEQUENCE num: NAT OF Processor ]; InternalControl: TYPE = {run, step, pause, stop}; DumpRequest: TYPE = {none, words, bytes, regs, stats, ring}; PauseCondTable: TYPE = REF PauseCondTableRep; PauseCondTableRep: TYPE = RECORD [ instArray: PACKED ARRAY Inst OF BOOL _ ALL[FALSE], pcRanges: LIST OF PCRange _ NIL, instCountStop: LIST OF INT _ NIL ]; InstCountTable: TYPE = REF InstCountTableRep; InstCountTableRep: TYPE = LizardToolOutput.InstCountTableRep; PCRange: TYPE = RECORD [firstPC,lastPC: LONG CARDINAL]; LastInstTable: TYPE = REF LastInstTableRep; LastInstTableRep: TYPE = LizardToolOutput.LastInstTableRep; LastInstMod: NAT = LizardToolOutput.LastInstMod; instCacheLines: NAT _ 64; dataCacheLines: NAT _ 64; SampleStats: ENTRY PROC [data: ToolData] RETURNS [ToolStats] = { RETURN [data.newStats]; }; UpdateStats: ENTRY PROC [data: ToolData, stats: ToolStats, updateTime: BOOL _ TRUE] = { IF updateTime THEN stats.samplePulses _ BasicTime.GetClockPulses[]; data.newStats _ stats; }; SetBaseState: ENTRY PROC [data: ToolData] = { data.baseStats _ data.newStats; data.baseStats.samplePulses _ BasicTime.GetClockPulses[]; }; GetDeltaFromBase: ENTRY PROC [data: ToolData] RETURNS [stats: ToolStats] = { stats.pStats.instructions _ data.newStats.pStats.instructions - data.baseStats.pStats.instructions; stats.pStats.cycles _ data.newStats.pStats.cycles - data.baseStats.pStats.cycles; stats.pStats.euFetches _ data.newStats.pStats.euFetches - data.baseStats.pStats.euFetches; stats.pStats.euStores _ data.newStats.pStats.euStores - data.baseStats.pStats.euStores; stats.pStats.instBytes _ data.newStats.pStats.instBytes - data.baseStats.pStats.instBytes; stats.samplePulses _ data.newStats.samplePulses - data.baseStats.samplePulses; }; Start: ENTRY PROC [data: ToolData] = { data.newStats _ [samplePulses: BasicTime.GetClockPulses[]]; data.live _ TRUE; BROADCAST data.refresh; }; Stop: ENTRY PROC [data: ToolData] = { vTab: VTables.VTable _ data.vTab; IF vTab # NIL THEN {data.vTab _ NIL; VTables.SetRowsAndColumns[vTab, 0, 0]}; IF data.log # NIL THEN { IO.Close[data.log ! IO.Error => CONTINUE]; data.log _ NIL; data.logTS _ NIL}; data.processors _ NIL; data.vTab _ NIL; data.pauseCondText _ NIL; data.pauseCondTable _ NIL; data.traceButton _ NIL; data.baseButton _ NIL; data.statusLabel _ NIL; data.live _ FALSE; data.instTable _ NIL; }; SetInternalControl: ENTRY PROC [data: ToolData, internalControl: InternalControl] = TRUSTED { data.internalControl _ internalControl; BROADCAST data.controlChange; }; WaitForControlChange: ENTRY PROC [data: ToolData] RETURNS [InternalControl] = TRUSTED { Process.SetTimeout[@data.controlChange, Process.MsecToTicks[data.refreshMillis]]; WAIT data.controlChange; RETURN [data.internalControl]; }; WaitForRefresh: ENTRY PROC [data: ToolData] = TRUSTED { Process.SetTimeout[@data.refresh, Process.MsecToTicks[data.refreshMillis]]; WAIT data.refresh; }; CauseRefresh: ENTRY PROC [data: ToolData] = TRUSTED { BROADCAST data.refresh; }; InvertTracing: ENTRY PROC [data: ToolData] = TRUSTED { data.tracing _ NOT data.tracing; Buttons.SetDisplayStyle[data.traceButton, IF data.tracing THEN $WhiteOnBlack ELSE $BlackOnWhite]; BROADCAST data.refresh; BROADCAST data.controlChange; }; InvertOutputBase: ENTRY PROC [data: ToolData] = TRUSTED { useHex: BOOL _ data.useHex _ NOT data.useHex; SetOutputBaseW[data.log, useHex]; Buttons.ReLabel[data.baseButton, IF useHex THEN " Hex " ELSE "Octal"]; BROADCAST data.refresh; BROADCAST data.controlChange; }; SetOutputBaseW: PROC [st: STREAM, useHex: BOOL] = TRUSTED { pfProcs: IOUtils.PFProcs = IOUtils.CopyPFProcs[st]; [] _ IOUtils.SetPFCodeProc[ pfProcs, 'w, IF useHex THEN CodeWHexProc ELSE CodeWOctalProc]; [] _ IOUtils.SetPFProcs[st, pfProcs]; }; CodeWHexProc: IOUtils.PFCodeProc = { IO.PutF[stream, "%xH", val]; }; CodeWOctalProc: IOUtils.PFCodeProc = { IO.PutF[stream, "%bB", val]; }; StopMenuProc: Menus.MenuProc = TRUSTED { SetInternalControl[NARROW[clientData], stop]; }; PauseMenuProc: Menus.MenuProc = TRUSTED { SetInternalControl[NARROW[clientData], pause]; }; RunMenuProc: Menus.MenuProc = TRUSTED { td: ToolData = NARROW[clientData]; IF shift AND td.tracing THEN InvertTracing[td]; SetInternalControl[td, run]; }; StepMenuProc: Menus.MenuProc = TRUSTED { SetInternalControl[NARROW[clientData], step]; }; ResetMenuProc: Menus.MenuProc = TRUSTED { td: ToolData = NARROW[clientData]; td.requestReset _ TRUE; }; ReschedMenuProc: Menus.MenuProc = TRUSTED { td: ToolData = NARROW[clientData]; td.requestReschedule _ TRUE; }; StopPrintMenuProc: Menus.MenuProc = TRUSTED { td: ToolData = NARROW[clientData]; td.stopPrint _ TRUE; }; DumpMemMenuProc: Menus.MenuProc = TRUSTED { td: ToolData = NARROW[clientData]; IF shift THEN td.requestDump _ bytes ELSE td.requestDump _ words; }; DumpRegsMenuProc: Menus.MenuProc = TRUSTED { td: ToolData = NARROW[clientData]; td.requestDump _ regs; }; DumpStatsMenuProc: Menus.MenuProc = TRUSTED { td: ToolData = NARROW[clientData]; td.requestDump _ stats; }; DumpRingMenuProc: Menus.MenuProc = TRUSTED { td: ToolData = NARROW[clientData]; td.requestDump _ ring; }; BiasStatsMenuProc: Menus.MenuProc = TRUSTED { td: ToolData = NARROW[clientData]; td.requestBiasStats _ TRUE; }; SamplePauseCondProc: Menus.MenuProc = TRUSTED { ShowPauseCond[NARROW[clientData]] }; SetPauseCondProc: Menus.MenuProc = TRUSTED { td: ToolData = NARROW[clientData]; newPauseData: PauseCondTable _ NEW[PauseCondTableRep _ []]; oldPauseData: PauseCondTable = td.pauseCondTable; st: STREAM = IO.RIS[ViewerTools.GetContents[td.pauseCondText]]; buffer: REF TEXT _ NEW[TEXT[64]]; -- buffer for scanning opNameTable: HandCodingUtil.NameArray = HandCodingUtil.GetInstArray[]; startPC: LONG CARDINAL; haveStart: BOOL _ FALSE; haveDots: BOOL _ FALSE; instCount: BOOL _ FALSE; IF shift AND oldPauseData # NIL THEN newPauseData^ _ oldPauseData^; DO tokenKind: IO.TokenKind; token: ROPE _ NIL; [tokenKind: tokenKind, token: token] _ IO.GetCedarTokenRope[st ! IO.EndOfStream => {tokenKind _ tokenEOF; CONTINUE}; IO.Error => {tokenKind _ tokenERROR; CONTINUE}]; {endPC: LONG CARDINAL _ startPC; SELECT tokenKind FROM tokenID => { IF haveDots THEN GO TO oops; FOR inst: Inst IN Inst DO IF Rope.Equal[token, opNameTable[inst]] THEN { newPauseData.instArray[inst] _ TRUE; EXIT; }; ENDLOOP; IF NOT haveStart THEN LOOP; -- No lagging PC to enter }; tokenSINGLE => { IF Rope.Fetch[token, 0] = '# THEN instCount _ TRUE; LOOP; }; tokenDOUBLE => { IF haveDots OR NOT haveStart THEN GO TO oops; IF NOT Rope.Equal[token, ".."] THEN GO TO oops; haveDots _ TRUE; LOOP; }; tokenDECIMAL, tokenOCTAL, tokenHEX => { ENABLE Convert.Error => GO TO oops; thisPC: LONG CARDINAL _ 0; SELECT tokenKind FROM tokenDECIMAL => thisPC _ Convert.CardFromDecimalLiteral[token]; tokenOCTAL => thisPC _ Convert.CardFromOctalLiteral[token]; tokenHEX => thisPC _ Convert.CardFromHexLiteral[token]; ENDCASE => ERROR; SELECT TRUE FROM instCount => { newPauseData.instCountStop _ CONS[thisPC, newPauseData.instCountStop]; instCount _ FALSE; LOOP; }; haveDots => { endPC _ thisPC; }; ENDCASE => { IF haveStart THEN { endPC _ startPC; newPauseData.pcRanges _ CONS[[startPC, endPC], newPauseData.pcRanges]; }; haveStart _ TRUE; startPC _ thisPC; LOOP; }; }; tokenEOF => { IF NOT haveStart THEN EXIT; IF haveDots THEN GO TO oops; haveStart _ FALSE; }; ENDCASE => GO TO oops; newPauseData.pcRanges _ CONS[[startPC, endPC], newPauseData.pcRanges]; haveStart _ haveDots _ instCount _ FALSE; EXITS oops => { MessageWindow.Append[ IO.PutFR["near pos %g, scanning error for pause condition", [integer[IO.GetIndex[st]]]], TRUE]; MessageWindow.Blink[]; RETURN; }; }; ENDLOOP; td.pauseCondTable _ newPauseData; ShowPauseCond[td]; }; ShowPauseCond: PROC [td: ToolData] = { oldPauseData: PauseCondTable = td.pauseCondTable; ros: STREAM = IO.ROS[]; opNameTable: HandCodingUtil.NameArray = HandCodingUtil.GetInstArray[]; SetOutputBaseW[ros, td.useHex]; IF oldPauseData # NIL THEN { FOR inst: Inst IN Inst DO IF oldPauseData.instArray[inst] THEN { IO.PutRope[ros, opNameTable[inst]]; IO.PutRope[ros, " "]; }; ENDLOOP; FOR each: LIST OF PCRange _ oldPauseData.pcRanges, each.rest WHILE each # NIL DO pcr: PCRange _ each.first; IF pcr.firstPC # pcr.lastPC THEN IO.PutF[ros, "%w..%w ", [cardinal[pcr.firstPC]], [cardinal[pcr.lastPC]] ] ELSE IO.PutF[ros, "%w ", [cardinal[pcr.firstPC]]]; ENDLOOP; FOR each: LIST OF INT _ oldPauseData.instCountStop, each.rest WHILE each # NIL DO IO.PutF[ros, "#%g ", [cardinal[each.first]]]; ENDLOOP; }; ViewerTools.SetContents[td.pauseCondText, IO.RopeFromROS[ros]]; }; TraceButtonProc: Menus.MenuProc = TRUSTED { InvertTracing[NARROW[clientData]]; }; BaseButtonProc: Menus.MenuProc = TRUSTED { InvertOutputBase[NARROW[clientData]]; }; SetDumpStartButtonProc: Menus.MenuProc = TRUSTED { data: ToolData = NARROW[clientData]; data.dumpStart _ NumberFromSelection[data.dumpStart]; }; SetDumpWordsButtonProc: Menus.MenuProc = TRUSTED { data: ToolData = NARROW[clientData]; data.dumpWords _ NumberFromSelection[data.dumpWords]; }; NumberFromSelection: PROC [default: INT] RETURNS [INT] = { selection: ROPE = ViewerTools.GetSelectionContents[]; RETURN [Convert.IntFromRope[selection ! Convert.Error => CONTINUE]]; MessageWindow.Append["Selection not a number!", TRUE]; MessageWindow.Blink[]; RETURN [default]; }; LogRegChange: LizardHeart.RegChangeProc = { td: ToolData = NARROW[data]; IF td.tracing THEN { log: STREAM = td.log; IO.PutRope[log, " "]; IO.PutRope[log, HandCodingUtil.GetRegArray[][reg]]; PutAsBoth[log, ", old: %w (%d)", old]; PutAsBoth[log, ", new: %w (%d)\n", new]; }; }; LogMemChange: LizardHeart.MemChangeProc = { td: ToolData = NARROW[data]; IF td.tracing THEN { log: STREAM = td.log; PutAsCard[log, " word addr: %w", addr]; PutAsBoth[log, ", old: %w (%g)", old]; PutAsBoth[log, ", new: %w (%g)\n", new]; }; }; LogIOChange: LizardHeart.IOChangeProc = { td: ToolData = NARROW[data]; IF td.tracing THEN { log: STREAM = td.log; PutAsCard[log, " IO addr: %w", addr]; PutAsBoth[log, ", old: %w (%g)", old]; PutAsBoth[log, ", new: %w (%g)\n", new]; }; }; LogInstStart: LizardHeart.InstStartProc = { td: ToolData = NARROW[data]; thisOne: LONG CARDINAL = DragOpsCrossUtils.WordToCard[thisPC]; instTable: InstCountTable = td.instTable; last: [0..LastInstMod) _ td.lastInstTable.last; td.lastInstTable.last _ last _ IF last = LastInstMod-1 THEN 0 ELSE last + 1; instTable[inst] _ instTable[inst] + 1; td.lastInstTable.pcArray[last] _ thisPC; {pauseTable: PauseCondTable = td.pauseCondTable; IF pauseTable # NIL THEN { IF pauseTable.instArray[inst] THEN GO TO setPause; FOR each: LIST OF PCRange _ pauseTable.pcRanges, each.rest WHILE each # NIL DO pcr: PCRange _ each.first; IF thisOne IN [pcr.firstPC..pcr.lastPC] THEN GO TO setPause; ENDLOOP; FOR each: LIST OF INT _ pauseTable.instCountStop, each.rest WHILE each # NIL DO IF processor.stats.instructions = each.first THEN GO TO setPause; ENDLOOP; }; SELECT inst FROM dTrap, LAST[Inst] => GO TO setPause; ENDCASE; EXITS setPause => { IF NOT td.tracing THEN InvertTracing[td]; SetInternalControl[td, pause]; }; }; IF td.tracing THEN { IO.PutF[td.log, "Inst %g start, cycle: %g, pc: %w, opcode: %w\n ", [integer[processor.stats.instructions]], [integer[processor.stats.cycles]], [cardinal[thisOne]], [cardinal[LOOPHOLE[inst, CARDINAL]]] ]; HandCodingUtil.ToStream[td.log, inst, rest, thisPC]; IO.PutRope[td.log, "\n"]; }; }; LogInstDone: LizardHeart.InstDoneProc = { td: ToolData = NARROW[data]; td.newStats.pStats _ processor.stats; td.newStats.samplePulses _ BasicTime.GetClockPulses[]; IF td.tracing THEN { log: STREAM = td.log; ctrl: ROPE = SELECT control FROM nextInst => "nextInst", doCall => "doCall", doReturn => "doReturn", doSwitch => "doSwitch", doAbort => "doAbort", ENDCASE => "??"; PutAsCard[log, " Inst done. newPC: %w", newPC]; PutAsCard[log, ", rtnPC: %w", rtnPC]; PutAsCard[log, ", regS: %w", processor.regs[ifuS]]; PutAsCard[log, ", regL: %w", processor.regs[ifuL]]; IO.PutF[log, ", control: %g\n\n", [rope[ctrl]] ]; }; }; GetInstAndRest: PROC [mem: SparseMemory.Base, pc: Word] RETURNS [inst: Inst, rest: Word] = { lastAddr: Word _ ZerosWord; bytes: DragOpsCross.FourBytes _ LOOPHOLE[ZerosWord]; instLen: NAT; word: Word; FetchByte: PROC [offset: NAT] RETURNS [Byte] = { wordAddr: Word; byteIndex: [0..3]; [wordAddr, byteIndex] _ DragOpsCrossUtils.BytePCToWordAddress[[DragOpsCrossUtils.AddDelta[offset, pc]]]; IF lastAddr # wordAddr THEN word _ SparseMemory.Fetch[mem, lastAddr _ wordAddr]; RETURN [LOOPHOLE[word, DragOpsCross.FourBytes][byteIndex]]; }; inst _ LOOPHOLE[FetchByte[0]]; instLen _ LOOPHOLE[inst, CARDINAL] / 64; IF instLen = 0 THEN instLen _ IF LOOPHOLE[inst, CARDINAL] < 40B THEN 1 ELSE 5; SELECT instLen FROM 2 => bytes[3] _ FetchByte[1]; 3 => { bytes[2] _ FetchByte[1]; bytes[3] _ FetchByte[2]; }; 5 => { bytes[0] _ FetchByte[1]; bytes[1] _ FetchByte[2]; bytes[2] _ FetchByte[3]; bytes[3] _ FetchByte[4]; }; ENDCASE; RETURN [inst, LOOPHOLE[bytes]]; }; PutAsBoth: PROC [log: STREAM, format: ROPE, word: Word] = { IO.PutF[log, format, [cardinal[DragOpsCrossUtils.WordToCard[word]]], [integer[DragOpsCrossUtils.WordToInt[word]]]]; }; PutAsCard: PROC [log: STREAM, format: ROPE, word: Word] = { IO.PutF[log, format, [cardinal[DragOpsCrossUtils.WordToCard[word]]]]; }; StandardNumberLabel: PROC [parent: Viewer, chars: NAT] RETURNS [NumberLabel] = { RETURN [NumberLabels.CreateNumber[ info: [parent: parent, border: FALSE], chars: chars, paint: FALSE]]; }; LizardToolWatcher: PROC [data: ToolData] = { NewMenuEntry: PROC [name: ROPE, proc: Menus.MenuProc, line: INTEGER _ 0] = { Menus.AppendMenuEntry[ menu, Menus.CreateEntry[name: name, proc: proc, clientData: data], line]; }; StandardButton: PROC [name: ROPE, proc: Menus.MenuProc, x,y: INTEGER, w: INTEGER _ 0] RETURNS [button: Buttons.Button] = { button _ Buttons.Create[ info: [parent: topViewer, name: name, border: TRUE, wx: x, wy: y, ww: w], proc: proc, clientData: data, paint: FALSE]; nextX _ button.wx+button.ww-1; nextY _ button.wy+button.wh-1; }; nextX: INTEGER _ 1; nextY: INTEGER _ 1; menu: Menus.Menu = Menus.CreateMenu[2]; topViewer: Viewer = Containers.Create[ info: [name: "LizardTool", iconic: TRUE, column: left, scrollable: FALSE], paint: FALSE]; traceButton: Buttons.Button _ data.traceButton _ StandardButton[ " Trace ", TraceButtonProc, 1, 1]; baseButton: Buttons.Button _ data.baseButton _ StandardButton[ " Octal ", BaseButtonProc, nextX, 1]; setDumpStartButton: Buttons.Button = StandardButton[ " Set Dump Start ", SetDumpStartButtonProc, nextX, 1]; setDumpWordsButton: Buttons.Button = StandardButton[ " Set Dump Words ", SetDumpWordsButtonProc, nextX, 1]; setPauseCondButton: Button = StandardButton[ " SetPauseCond ", SetPauseCondProc, 1, nextY]; samplePauseCondButton: Button = StandardButton[ " Sample ", SamplePauseCondProc, 1, nextY, setPauseCondButton.ww]; pauseCondText: Viewer = ViewerOps.CreateViewer[ flavor: $Text, info: [ wx: nextX, wy: setPauseCondButton.wy, ww: setPauseCondButton.ww, wh: nextY+1 - setPauseCondButton.wy, parent: topViewer], paint: FALSE]; vTab: VTable = VTables.Create[columns: 10, rows: 3, parent: topViewer, x: 1, y: nextY]; statusLabel: Label _ data.statusLabel _ Labels.Create[ info: [name: "Initializing", parent: vTab, border: FALSE], paint: FALSE]; instCountLabel: NumberLabel = StandardNumberLabel[vTab, 10]; instRateLabel: NumberLabel = StandardNumberLabel[vTab, 7]; cycleCountLabel: NumberLabel = StandardNumberLabel[vTab, 10]; cycleRateLabel: NumberLabel = StandardNumberLabel[vTab, 7]; iBytesCountLabel: NumberLabel = StandardNumberLabel[vTab, 10]; iBytesRateLabel: NumberLabel = StandardNumberLabel[vTab, 7]; readCountLabel: NumberLabel = StandardNumberLabel[vTab, 10]; readRateLabel: NumberLabel = StandardNumberLabel[vTab, 7]; writeCountLabel: NumberLabel = StandardNumberLabel[vTab, 10]; writeRateLabel: NumberLabel = StandardNumberLabel[vTab, 7]; DoRefresh: PROC [data: ToolData] = { newStats: ToolStats = SampleStats[data]; deltaStats: ToolStats = GetDeltaFromBase[data]; IF deltaStats.samplePulses # 0 THEN { secs: REAL = BasicTime.PulsesToSeconds[deltaStats.samplePulses]; invSecs: REAL = 1/secs; IF deltaStats.pStats.instructions # 0 THEN { NumberLabels.NumberLabelUpdate[instCountLabel, newStats.pStats.instructions]; NumberLabels.NumberLabelUpdate[instRateLabel, Real.RoundLI[(deltaStats.pStats.instructions) * invSecs]]; }; IF deltaStats.pStats.cycles # 0 THEN { NumberLabels.NumberLabelUpdate[cycleCountLabel, newStats.pStats.cycles]; NumberLabels.NumberLabelUpdate[cycleRateLabel, Real.RoundLI[(deltaStats.pStats.cycles) * invSecs]]; }; IF deltaStats.pStats.instBytes # 0 THEN { NumberLabels.NumberLabelUpdate[iBytesCountLabel, newStats.pStats.instBytes]; NumberLabels.NumberLabelUpdate[iBytesRateLabel, Real.RoundLI[(deltaStats.pStats.instBytes) * invSecs]]; }; IF deltaStats.pStats.euFetches # 0 THEN { NumberLabels.NumberLabelUpdate[readCountLabel, newStats.pStats.euFetches]; NumberLabels.NumberLabelUpdate[readRateLabel, Real.RoundLI[(deltaStats.pStats.euFetches) * invSecs]]; }; IF deltaStats.pStats.euStores # 0 THEN { NumberLabels.NumberLabelUpdate[writeCountLabel, newStats.pStats.euStores]; NumberLabels.NumberLabelUpdate[writeRateLabel, Real.RoundLI[(deltaStats.pStats.euStores) * invSecs]]; }; }; }; topViewer.menu _ menu; NewMenuEntry["Stop!", StopMenuProc]; NewMenuEntry["Pause", PauseMenuProc]; NewMenuEntry["Run", RunMenuProc]; NewMenuEntry["Step", StepMenuProc]; NewMenuEntry["Reset", ResetMenuProc]; NewMenuEntry["Resched", ReschedMenuProc]; NewMenuEntry["StopPrint!", StopPrintMenuProc]; NewMenuEntry["DumpMem", DumpMemMenuProc, 1]; NewMenuEntry["DumpRegs", DumpRegsMenuProc, 1]; NewMenuEntry["DumpStats", DumpStatsMenuProc, 1]; NewMenuEntry["DumpRing", DumpRingMenuProc, 1]; NewMenuEntry["BiasStats", BiasStatsMenuProc, 1]; VTables.SetTableEntry[ table: vTab, row: 0, column: 0, flavor: $Viewer, clientData: statusLabel]; VTables.SetTableEntry[table: vTab, row: 1, column: 0, name: "count"]; VTables.SetTableEntry[table: vTab, row: 2, column: 0, name: "rate"]; VTables.SetTableEntry[table: vTab, row: 0, column: 1, name: "inst"]; VTables.SetTableEntry[ table: vTab, row: 1, column: 1, flavor: $Viewer, clientData: instCountLabel]; VTables.SetTableEntry[ table: vTab, row: 2, column: 1, flavor: $Viewer, clientData: instRateLabel]; VTables.SetTableEntry[table: vTab, row: 0, column: 2, name: "cycle"]; VTables.SetTableEntry[ table: vTab, row: 1, column: 2, flavor: $Viewer, clientData: cycleCountLabel]; VTables.SetTableEntry[ table: vTab, row: 2, column: 2, flavor: $Viewer, clientData: cycleRateLabel]; VTables.SetTableEntry[table: vTab, row: 0, column: 3, name: "instBytes"]; VTables.SetTableEntry[ table: vTab, row: 1, column: 3, flavor: $Viewer, clientData: iBytesCountLabel]; VTables.SetTableEntry[ table: vTab, row: 2, column: 3, flavor: $Viewer, clientData: iBytesRateLabel]; VTables.SetTableEntry[table: vTab, row: 0, column: 4, name: "read"]; VTables.SetTableEntry[ table: vTab, row: 1, column: 4, flavor: $Viewer, clientData: readCountLabel]; VTables.SetTableEntry[ table: vTab, row: 2, column: 4, flavor: $Viewer, clientData: readRateLabel]; VTables.SetTableEntry[table: vTab, row: 0, column: 5, name: "write"]; VTables.SetTableEntry[ table: vTab, row: 1, column: 5, flavor: $Viewer, clientData: writeCountLabel]; VTables.SetTableEntry[ table: vTab, row: 2, column: 5, flavor: $Viewer, clientData: writeRateLabel]; VTables.Install[table: vTab, paint: FALSE]; data.logTS _ TypeScript.Create[ info: [parent: topViewer, wx: vTab.wx, wy: vTab.wy+vTab.wh, border: FALSE], paint: FALSE]; TypeScript.ChangeLooks[data.logTS, 'f]; -- fixed width font for alignment TypeScript.ChangeLooks[data.logTS, 's]; -- small for compactness data.vTab _ vTab; data.pauseCondText _ pauseCondText; data.log _ ViewerIO.CreateViewerStreams[ name: "LizardTool.log", viewer: data.logTS, backingFile: "LizardTool.log", editedStream: FALSE].out; Containers.ChildXBound[container: topViewer, child: data.logTS]; Containers.ChildYBound[container: topViewer, child: data.logTS]; Containers.ChildXBound[container: topViewer, child: pauseCondText]; InvertOutputBase[data]; -- forces the initial base to be Octal ViewerOps.OpenIcon[topViewer]; Start[data]; WHILE data.live DO DoRefresh[data]; WaitForRefresh[data]; IF NOT data.live OR topViewer.destroyed THEN EXIT; ENDLOOP; }; ExecBase: PROC [nProcessors: NAT, area: HandCodingSupport.Area] = { data: ToolData = NEW[ToolDataRep]; processors: ProcessorSequence = NEW[ProcessorSequenceRep[nProcessors]]; logger: ChangeLogger = NEW[ChangeLoggerRep _ [ data, LogRegChange, LogMemChange, LogIOChange, LogInstStart, LogInstDone]]; cycles: INT _ 0; processor: Processor _ NIL; lagControl: InternalControl _ run; statusLabel: Label _ NIL; mem: SparseMemory.Base = NARROW[area.data]; shared: LizardCache.SharedBase = LizardCache.NewBase[mem]; instTable: InstCountTable _ NEW[InstCountTableRep _ ALL[0]]; logger.data _ data; data.instTable _ instTable; data.lastInstTable _ NEW[LastInstTableRep]; data.lastInstTable.last _ 0; data.lastInstTable.pcArray _ ALL[ZerosWord]; FOR i: NAT IN [0..nProcessors) DO ifuCache: LizardCache.CacheBase = LizardCache.NewCache[shared, instCacheLines]; euCache: LizardCache.CacheBase = LizardCache.NewCache[shared, dataCacheLines]; processors[i] _ LizardHeart.NewProcessor[ifuCache, euCache, logger]; ENDLOOP; TRUSTED {Process.Detach[FORK LizardToolWatcher[data]]}; UNTIL data.live DO WaitForRefresh[data]; ENDLOOP; statusLabel _ data.statusLabel; IO.PutF[data.log, "\nStarting interpreter at %g\n\n", [time[BasicTime.Now[]]] ]; Labels.Set[statusLabel, "Pausing"]; processor _ processors[0]; Process.SetPriority[Process.priorityBackground]; WHILE data.live AND NOT data.vTab.destroyed DO internalControl: InternalControl _ data.internalControl; SELECT TRUE FROM data.requestReset => { processor.resetRequested _ TRUE; IO.PutRope[data.log, "Note: reset requested by user.\n\n"]; data.requestReset _ FALSE; UpdateStats[data, SampleStats[data]]; LOOP; }; data.requestReschedule => { LOOPHOLE[processor.regs[ifuStatus], DragOpsCross.IFUStatusRec].reschedule _ TRUE; IO.PutRope[data.log, "Note: reschedule requested by user.\n\n"]; data.requestReschedule _ FALSE; LOOP; }; data.requestDump # none => { ServiceDumpRequest[data, processor, mem, data.requestDump]; data.requestDump _ none; LOOP; }; data.requestBiasStats => { ServiceBiasStatsRequest[data, processor]; data.requestBiasStats _ FALSE; LOOP; }; ENDCASE; IF internalControl # lagControl THEN { SELECT internalControl FROM run => Labels.Set[statusLabel, "Running"]; step => Labels.Set[statusLabel, "Stepping"]; pause => Labels.Set[statusLabel, "Pausing"]; stop => Labels.Set[statusLabel, "Stopping"]; ENDCASE => ERROR; lagControl _ internalControl; SetBaseState[data]; CauseRefresh[data]; }; SELECT internalControl FROM run, step => { }; pause => { UpdateStats[data, SampleStats[data]]; [] _ WaitForControlChange[data]; LOOP}; stop => EXIT; ENDCASE => ERROR; LizardHeart.InstructionExecute[processor ! LizardHeart.OutsideEnvelope => { data.log.PutF["Warning: In instruction %g processor outside defined envelope -\n %g\n\n", IO.int[processor.stats.instructions], IO.rope[explanation]]; internalControl _ step; RESUME } ]; IF internalControl = step THEN { SetInternalControl[data, pause]; CauseRefresh[data]; }; ENDLOOP; IO.PutF[data.log, "\nStopping interpreter at %g\n\n", [time[BasicTime.Now[]]] ! IO.Error => CONTINUE]; Stop[data]; IF NOT statusLabel.destroyed THEN Labels.Set[statusLabel, "Stopped"]; }; ServiceDumpRequest: PROC [data: ToolData, processor: Processor, mem: SparseMemory.Base, kind: DumpRequest] = { regNameArray: HandCodingUtil.RegNameArray = HandCodingUtil.GetRegArray[]; instNameArray: HandCodingUtil.NameArray = HandCodingUtil.GetInstArray[]; log: STREAM = data.log; testAbort: WriteSparseMemory.TestAbort = {RETURN [data.stopPrint]}; data.stopPrint _ FALSE; IO.PutRope[data.log, "Note: start of dump requested by user.\n"]; SELECT kind FROM words, bytes => { useBytes: BOOL = kind = bytes; IO.PutRope[log, "\n"]; LizardCache.FlushCache[processor.euCache]; WriteSparseMemory.ToStream[ data.log, mem, DragOpsCrossUtils.IntToWord[data.dumpStart], data.dumpWords, useBytes, useBytes, testAbort]; }; regs => LizardToolOutput.ShowRegisters[log, processor, testAbort]; ring => LizardToolOutput.ShowRing[log, data.lastInstTable, mem, LastInstMod, testAbort]; stats => LizardToolOutput.ShowStats[log, processor, data.instTable, testAbort]; ENDCASE; IO.PutRope[data.log, "\nNote: end of dump requested by user.\n\n"]; data.stopPrint _ FALSE; }; ServiceBiasStatsRequest: PROC [data: ToolData, processor: LizardHeart.Processor] = { testAbort: WriteSparseMemory.TestAbort = {RETURN [data.stopPrint]}; newBias: LizardToolOutput.StatSnapshot _ [pStats: processor.stats, discarded: processor.instBuffer.bytesDiscarded, -- not in stats!!! ifuStats: processor.ifuCache.stats, euStats: processor.euCache.stats, iStats: data.instTable^]; LizardToolOutput.ShowBiasedStats[data.log, data.biasStats, newBias]; data.biasStats _ newBias; IO.PutRope[data.log, "\nNote: Statistics biased.\n\n"]; }; Exec: PROC [nProcessors: NAT _ 1] = TRUSTED { area: HandCodingSupport.Area = HandCodingSupport.GetCurrentArea[]; Process.Detach[FORK ExecBase[nProcessors, area]]; }; Exec1: PROC = { Exec[1]; }; Exec2: PROC = { Exec[2]; }; Exec3: PROC = { Exec[3]; }; Exec4: PROC = { Exec[4]; }; END. jLizardToolDriver.mesa Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Ed McCreight, January 8, 1986 12:09:58 pm PST Russ Atkinson (RRA) February 14, 1986 7:40:04 pm PST Peter Kessler November 11, 1985 3:09:29 pm PST ENTRY procs to manipulate the tool state atomically Call this proc to stop execution for good. We go around storing NIL to assist the garbage collector. Menu procs to respond to mouse clicks [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] if shift was held, add to old stuff This should be an instruction name. Of course we may have a pending startPC as well. Look up the instruction name and set the appropriate bits We must have a startPC to accept this token We must have a startPC to accept this token This should be the start of a PC range When we get this far we have a new PC range to add [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] [parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL] Change logging procs [data: REF, processor: Processor, reg: ProcessorRegister, old,new: Word] [data: REF, processor: Processor, addr: Word, old,new: Word] [data: REF, processor: Processor, addr: Word, old,new: Word] [data: REF, processor: Processor, thisPC: Word, inst: Inst, rest: Word] Check for instructions that must pause Check the PC ranges Check the instruction count [data: REF, processor: Processor, newPC, rtnPC: Word, control: Control, cycles: INT] Utility procs A slow but simple routine to fetch instructions and the rest given the PC. Main procs Now we are off and running! Wait for the watcher to start up For now, we only support one processor When we are really cranking along in the instruction stream we don't want to hog the machine. Cedar does not really handle competing processes at the same priority level as well as we would like. Now start interpreting instructions until told to stop (currently we only support one processor). Show the new status to make the memory agree with what is in the cache Dump the last N instructions Dump the current statistics Snapshot the statistics display the statistics save the bias The following procedures are for the convenience of the Quad command. Ê"«˜codešœ™Kšœ Ïmœ=™HKšœ-™-K™4K™.—˜šÏk ˜ Kšœ žœ0˜?Kšœžœ,˜9Kšœ žœ$˜4Kšœžœ^˜kKšœ žœU˜gKšœžœC˜ZKšœžœ˜/Kšœžœ@˜TKšœ žœ˜Kš žœžœUžœžœžœ ˜Kšœžœ?˜LKšœžœ˜"Kšœ žœ8˜IKšœ žœÁ˜ÒK˜Kšœžœ<˜GKšœžœ˜$Kšœ žœ0˜BKšœžœD˜QKšœžœ ˜Kšœžœžœ˜ Kšœ žœ˜!Kšœ žœ˜'Kšœžœ˜Kšœžœ ˜Kšœ žœ˜%Kšœ žœ˜)Kšœ žœ2˜CKšœžœ=˜JKšœžœ˜.——headšœžœž˜Kšžœžœ˜KšžœPžœÞ˜·Kšœž˜K˜Kšœžœ˜$Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜.Kšœžœ˜4Kšœ žœ˜$Kšœžœ˜Kšœžœ˜Kšœ žœ˜-Kšœ žœ˜(Kšœžœ"˜9Kšœžœ˜2Kšžœžœžœ˜Kšžœžœžœžœ˜Kšœ žœ˜.Kšœžœ˜$Kšœžœ˜šœžœ˜Kšœ)˜)Kšœ'˜'—K˜Kšœ žœžœ ˜!šœ žœž œžœ˜&Kšœžœ˜Kšœ žœ˜$Kšœ žœ˜Kšœžœ˜Kšœžœ˜Kšœžœžœ˜Kšœ*žœ˜.Kšœžœ˜Kšœžœ˜Kšœ!žœ˜%Kšœžœ˜Kšœ ž œ˜Kšœž œ˜Kšœ)˜)K˜:Kšœ žœ˜Kšœ žœžœžœ˜Kšœžœžœ˜Kšœ!žœžœ˜.Kšœ ˜ Kšœžœžœ˜Kšœ žœžœ˜Kšœ žœžœ˜KšœžœžœÏc*˜@Kšœžœ˜ Kšœžœ˜#K˜$K˜(K˜K˜—šœ žœžœ˜Kšœ#˜#Kšœ˜K˜K˜—Kšœžœžœ˜3šœžœžœ˜%Kšžœžœžœ ˜K˜K˜—Kšœžœ˜1K˜Kšœ žœ+˜—Kšœ%˜%K˜K˜—šœ$˜$Kšžœ˜K˜K˜—šœ&˜&Kšžœ˜K˜K˜——šœ%™%K˜šœžœ˜(Kšœ žœžœ,žœ™NKšœžœ˜-K˜K˜—šœ žœ˜)Kšœ žœžœ,žœ™NKšœžœ˜.K˜K˜—šœžœ˜'Kšœ žœžœ,žœ™NKšœžœ ˜"Kšžœžœ žœ˜/Kšœ˜K˜K˜—šœžœ˜(Kšœ žœžœ,žœ™NKšœžœ˜-K˜K˜—šœ žœ˜)Kšœ žœžœ,žœ™NKšœžœ ˜"Kšœžœ˜K˜K˜—šœ"žœ˜+Kšœ žœžœ,žœ™NKšœžœ ˜"Kšœžœ˜K˜K˜—šœ$žœ˜-Kšœ žœžœ,žœ™NKšœžœ ˜"Kšœžœ˜K˜K˜—šœ"žœ˜+Kšœ žœžœ,žœ™NKšœžœ ˜"Kšžœžœžœ˜AK˜K˜—šœ#žœ˜,Kšœ žœžœ,žœ™NKšœžœ ˜"Kšœ˜K˜K˜—šœ$žœ˜-Kšœ žœžœ,žœ™NKšœžœ ˜"Kšœ˜K˜K˜—šœ#žœ˜,Kšœ žœžœ,žœ™NKšœžœ ˜"Kšœ˜K˜K˜—šœ$žœ˜-Kšœ žœžœ,žœ™NKšœžœ ˜"Kšœžœ˜K˜K˜—šœ&žœ˜/Kšœ žœžœ,žœ™NKšœžœ ˜!K˜K˜—šœ#žœ˜,Kšœ žœžœ,žœ™NKšœžœ ˜"Kšœžœ˜;Kšœ1˜1Kšœžœžœžœ,˜?Kš œžœžœžœžœŸ˜9KšœF˜FKšœ žœžœ˜Kšœ žœžœ˜Kšœ žœžœ˜Kšœ žœžœ˜K˜šžœžœžœžœ˜CKšœ#™#—K˜šž˜Kšœ žœ ˜Kšœžœžœ˜šœ$˜$šœžœ˜šœžœ'žœ˜5Kšžœ#žœ˜0———šœžœžœ ˜ šžœ ž˜˜ KšœU™UKšžœ žœžœžœ˜K˜Kšœ9™9šžœ žœž˜šžœ&žœ˜.Kšœžœ˜$Kšžœ˜K˜—Kšžœ˜K˜—Kš žœžœ žœžœŸ˜6K˜—šœ˜Kšœ+™+Kšžœžœ žœ˜3Kšžœ˜K˜—šœ˜Kšœ+™+Kš žœ žœžœ žœžœžœ˜-Kš žœžœžœžœžœ˜/Kšœ žœ˜Kšžœ˜K˜—šœ'˜'Kšœ&™&Kšžœžœžœ˜#Kšœžœžœ˜šžœ ž˜Kšœ?˜?Kšœ;˜;Kšœ7˜7Kšžœžœ˜—šžœžœž˜šœ˜Kšœžœ%˜FKšœ žœ˜Kšžœ˜K˜—šœ ˜ Kšœ˜K˜—šžœ˜ šžœ žœ˜Kšœ˜Kšœžœ*˜FK˜—Kšœ žœ˜Kšœ˜Kšžœ˜K˜——K˜—šœ ˜ Kšžœžœ žœžœ˜Kšžœ žœžœžœ˜Kšœ žœ˜Kšœ˜—Kšžœžœžœ˜—K˜Kšœ2™2Kšœžœ*˜FKšœ#žœ˜)K˜šžœ ˜šœ˜šžœ9˜;Kšœ žœ˜—Kšžœ˜—K˜Kšžœ˜K˜—K˜—Kšžœ˜—K˜Kšœ!˜!Kšœ˜K˜K˜—š  œžœ˜&Kšœ1˜1Kšœžœžœžœ˜KšœF˜FKšœ˜K˜šžœžœžœ˜šžœ žœž˜šžœžœ˜&Kšžœ!˜#Kšžœ˜K˜—Kšžœ˜—š žœžœžœ,žœžœž˜PKšœ˜šžœ˜KšžœžœG˜NKšžœžœ+˜2—Kšžœ˜—š žœžœžœžœ)žœžœž˜QKšžœ+˜-Kšžœ˜—K˜—Kšœ*žœ˜?K˜K˜—šœ"žœ˜+Kšœ žœžœ,žœ™NKšœžœ˜"K˜K˜—šœ!žœ˜*Kšœ žœžœ,žœ™NKšœžœ˜%K˜K˜—šœ)žœ˜2Kšœ žœžœ,žœ™NKšœžœ ˜$Kšœ5˜5K˜K˜—šœ)žœ˜2Kšœ žœžœ,žœ™NKšœžœ ˜$Kšœ5˜5K˜K˜—š  œžœ žœžœžœ˜:Kšœ žœ&˜5Kšžœ3žœ˜DKšœ0žœ˜6K˜Kšžœ ˜K˜K˜——šœ™K˜šœ+˜+Kšœžœ>™HKšœžœ˜šžœ žœ˜Kšœžœ ˜Kšžœ˜Kšžœ1˜3Kšœ&˜&Kšœ(˜(K˜—K˜K˜—šœ+˜+Kšœžœ2™Kšœ)˜)Kšœ/˜/Kšœžœžœžœ ˜LK˜Kšœ&˜&Kšœ(˜(K˜šœ0˜0K˜šžœžœžœ˜K˜Kšœ&™&Kšžœžœžœžœ ˜2K˜Kšœ™š žœžœžœ*žœžœž˜NKšœ˜Kš žœ žœžœžœžœ ˜˜>Kšœ%˜%—šœ4˜4Kšœ6˜6—šœ4˜4Kšœ6˜6—šœ,˜,Kšœ.˜.—šœ/˜/KšœB˜B—šœ/˜/Kšœ˜šœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ$˜$Kšœ˜—Kšœžœ˜—KšœW˜Wšœ6˜6Kšœ3žœ˜:Kšœžœ˜—Kšœ<˜˜>Kšœ<˜˜@Kšœžœ˜Kšžœ˜Kšœ˜—šœ˜Kšœ;˜;Kšœ˜Kšžœ˜Kšœ˜—šœ˜Kšœ)˜)Kšœžœ˜Kšžœ˜Kšœ˜—K˜Kšžœ˜—K˜šžœžœ˜&Kšœ™šžœž˜Kšœ*˜*Kšœ,˜,Kšœ,˜,Kšœ,˜,Kšžœžœ˜—Kšœ˜Kšœ˜Kšœ˜K˜—šžœž˜šœ˜Kšœ˜—šœ ˜ Kšœ%˜%Kšœ ˜ Kšžœ˜—Kšœžœ˜ Kšžœžœ˜—šœ*˜*šœ ˜ šœ[˜[Kšžœ$žœ˜<—Kšœ˜Kšž˜Kšœ˜—šœ˜K˜——šžœžœ˜ Kšœ ˜ Kšœ˜K˜K˜—Kšžœ˜—K˜šžœK˜MKšœžœ žœ˜—K˜ Kšžœžœžœ$˜EK˜K˜—š œžœV˜nKšœI˜IKšœH˜HKšœžœ ˜Kšœ*žœ˜CKšœžœ˜Kšžœ?˜Ašžœž˜šœ˜Kšœ žœ˜Kšžœ˜šœ*˜*Kšœ2™2—šœ˜Kšœ;˜;Kšœ/˜/—K˜—˜Kšœ:˜:—˜Kšœ™KšœP˜P—šœ˜Kšœ™KšœF˜F—Kšžœ˜—KšžœA˜CKšœžœ˜K˜—K˜š œžœ7˜TKš  œ!žœ˜CK™KšœsŸœ_˜åK™K˜DK™ Kšœ˜Kšžœ5˜7K˜K˜—K˜—šœE™EK˜š œžœžœžœ˜-KšœB˜BKšœžœ˜1K˜K˜—š œžœ˜Kšœ˜K˜—K˜š œžœ˜Kšœ˜K˜—K˜š œžœ˜Kšœ˜K˜—K˜š œžœ˜Kšœ˜K˜——K˜Kšžœ˜K˜—…—sâ£÷