DIRECTORY AMBridge, AMModel, AMModelBridge, AMTypes, Ascii, AssociativeCache, Atom, Basics, BasicTime, Buttons, CacheModels, Commander, CommandTool, Containers USING [ChildXBound, ChildYBound, Container, Create], Convert, DirectMapCache, Dragoman, DragomanOpDebug, DragomanPrivate, FS, IO, Labels, List, ListerUtils, MessageWindow, PrincOps, PrincOpsUtils, Process, ProcessProps, Rope, Rules USING [Create, Rule], SafeStorage, TypeScript, VFonts, ViewerClasses USING [Viewer, ViewerClassRec], ViewerIO USING [CreateViewerStreams], ViewerOps USING [PaintViewer], ViewerTools USING [GetContents, GetSelectionContents, MakeNewTextViewer, SetContents, SetSelection], WorldVM; DragomanDebug: CEDAR MONITOR LOCKS h.LOCK USING h: Handle IMPORTS AMBridge, AMModel, AMModelBridge, AMTypes, AssociativeCache, Atom, BasicTime, Buttons, Commander, CommandTool, Containers, Convert, DirectMapCache, DragomanOpDebug, DragomanPrivate, IO, Labels, List, MessageWindow, PrincOpsUtils, Process, ProcessProps, Rope, Rules, SafeStorage, TypeScript, VFonts, ViewerIO, ViewerOps, ViewerTools, WorldVM = BEGIN entryHeight: CARDINAL = 15; -- how tall to make each line of items entryVSpace: CARDINAL = 2; -- vertical leading space between lines entryHSpace: CARDINAL = 5; -- horizontal space between items in a line ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; TV: TYPE = AMTypes.TV; Byte: TYPE = Basics.Byte; Machine: TYPE = DragomanPrivate.Machine; FinishedExecution: PUBLIC SIGNAL = CODE; Handle: TYPE = REF MyRec; -- a REF to the data for a particular instance of the sample tool; multiple instances can be created. MyRec: TYPE = MONITORED RECORD [ -- the data for a particular tool instance outer: Containers.Container _ NIL, -- handle for the enclosing container height: INT _ 0, -- height measured from the top of the container cmd: CommandViewer, -- the commands busy, ready: BOOL _ FALSE, show2Above, traceOps, countOps, recordXfers, modelCache, flushOnCall: REF BOOL, stopFlag, killCountProcess: BOOL _ FALSE, running, pauseTime: CONDITION, m: DragomanPrivate.Machine, workingDir: Atom.PropList, parent: Commander.Handle, rc, mc: CacheModels.Cache, tsIn, tsOut: STREAM, ts: ViewerClasses.Viewer ]; -- the typescript CommandViewer: TYPE = RECORD [ iCount: ViewerClasses.Viewer, cacheLines, cacheQuads, instCaches, dataCaches: ViewerClasses.Viewer, pc: ViewerClasses.Viewer, lf, lfName: ViewerClasses.Viewer, gf, gfName: ViewerClasses.Viewer, nextOp: ViewerClasses.Viewer, stk: ARRAY [0..PrincOps.stackDepth) OF ViewerClasses.Viewer, lcl: ARRAY [0..PrincOps.stackDepth) OF ViewerClasses.Viewer, gfiRange: ViewerClasses.Viewer, breakGF, breakPC: ViewerClasses.Viewer, initialProc, configName: ViewerClasses.Viewer, commandLine: ViewerClasses.Viewer ]; PromptRec: TYPE = RECORD [ handle: Handle, viewer: ViewerClasses.Viewer _ NIL, number: BOOL _ FALSE]; BoolRec: TYPE = RECORD [ handle: Handle, flag: REF BOOL]; PromptHandle: TYPE = REF PromptRec; MakeTool: Commander.CommandProc = TRUSTED BEGIN rule: Rules.Rule; my: Handle _ NEW[MyRec]; myCache: DragomanPrivate.CacheInfo; cpl: Atom.PropList _ ProcessProps.GetPropList[]; wd: ROPE _ NARROW[List.Assoc[$WorkingDirectory, cpl]]; my.parent _ NARROW[List.Assoc[$CommanderHandle, cpl]]; my.workingDir _ Atom.PutPropOnList[propList: NIL, prop: $WorkingDirectory, val: wd]; my.outer _ Containers.Create[[-- construct the outer container name: "Dragoman", -- name displayed in the caption iconic: TRUE, -- so tool will be iconic (small) when first created column: left, -- initially in the left column scrollable: FALSE ]]; -- inhibit user from scrolling contents MakeCommands[my]; -- build each (sub)viewer in turn rule _ Rules.Create [[parent: my.outer, wy: my.height, ww: my.outer.cw, wh: 2]]; Containers.ChildXBound[my.outer, rule]; my.height _ my.height + entryHeight + 2; -- interline spacing MakeTypescript[my]; my.rc _ DirectMapCache.NewCache[lines: 512, quadsPerLine: 8]; -- real cache my.mc _ AssociativeCache.NewCache[lines: 256, quadsPerLine: 4, wordsPerQuad: 1]; -- map cache myCache _ NEW [DragomanPrivate.CacheInfoRec _ [iCaches: 1, dCaches: 1]]; myCache.iCache[0] _ AssociativeCache.NewCache[lines: 100, wordsPerQuad: 4, quadsPerLine: 1, lru: FALSE, realCache: my.rc, mapCache: my.mc]; myCache.dCache[0] _ AssociativeCache.NewCache[lines: 100, wordsPerQuad: 4, quadsPerLine: 1, lru: FALSE, realCache: my.rc, mapCache: my.mc]; my.m _ NEW[DragomanPrivate.MachineStateRec _ [ cacheData: myCache, history: NEW [DragomanPrivate.OpHistoryRec], opCount: NEW [ARRAY Byte OF INT], interestingGfi: NEW [DragomanPrivate.BitVector _ ALL[FALSE]]]]; ViewerOps.PaintViewer[my.outer, all]; -- reflect above change END; MakeTypescript: PROC [handle: Handle] = BEGIN handle.height _ handle.height + entryVSpace; -- space down from the top of the viewer handle.ts _ TypeScript.Create[ info: [name: "Dragoman.ts", wy: handle.height, parent: handle.outer, border: FALSE ]]; [handle.tsIn, handle.tsOut] _ ViewerIO.CreateViewerStreams [ name: "Dragoman.ts", viewer: handle.ts, backingFile: "Dragoman.ts", editedStream: FALSE]; Containers.ChildXBound[handle.outer, handle.ts]; Containers.ChildYBound[handle.outer, handle.ts]; END; SetValue: PROC [label: ViewerClasses.Viewer, value: ROPE] = { IF label # NIL THEN ViewerTools.SetContents[label, value]}; MakeCommands: PROC [handle: Handle] = BEGIN initialData: Rope.ROPE = NIL; topY: INT = handle.height; stkX, lclblX, lclX, tagX, valX, nameX: INT; trX, opsX, xfrX: INT; wy: INT _ topY; wx: INT _ 0; current: INT = INT.FIRST; At: PROC [x, row: INT _ current] = { IF row # current THEN { wy _ topY + row * (entryHeight + entryVSpace); handle.height _ MAX[handle.height, wy]}; IF x # current THEN wx _ x}; NewLine: PROC = { wy _ wy + entryHeight + entryVSpace; handle.height _ MAX[handle.height, wy]; wx _ entryHSpace}; NamedItem: PROC [label: ROPE, width: INT, data: ROPE _ NIL, number: BOOL _ FALSE] RETURNS [v: ViewerClasses.Viewer] = { ph: PromptHandle _ NEW [PromptRec _ [handle: handle]]; t: Buttons.Button _ Buttons.Create[ info: [ name: Rope.Concat[label, ":"], wy: wy, wh: entryHeight, -- specify rather than defaulting so line is uniform wx: wx, parent: handle.outer, border: FALSE ], proc: Prompt, clientData: ph]; -- this will be passed to our button proc wx _ wx + t.ww + entryHSpace; v _ ViewerTools.MakeNewTextViewer[ [ parent: handle.outer, wx: wx, wy: wy, ww: width*VFonts.CharWidth['0]+12, wh: entryHeight, data: data, scrollable: FALSE, border: FALSE]]; ph.viewer _ v; wx _ wx + v.ww + entryHSpace}; Label: PROC [value: ROPE, width: INT _ 0] RETURNS [v: ViewerClasses.Viewer] = { lw: INT _ MAX[ width*VFonts.CharWidth['0], IF value = NIL THEN 0 ELSE VFonts.StringWidth[value]]+4; v _ Labels.Create[ [ name: value, -- initial contents wx: wx, wy: wy, ww: lw, wh: entryHeight, parent: handle.outer, border: FALSE]]; wx _ wx + v.ww + entryHSpace}; Value: PROC [value: ROPE, width: INT _ 0] RETURNS [v: ViewerClasses.Viewer] = { lw: INT _ MAX[ width*VFonts.CharWidth['0], IF value = NIL THEN 0 ELSE VFonts.StringWidth[value]]+12; v _ ViewerTools.MakeNewTextViewer[ [ data: value, -- initial contents wx: wx, wy: wy, ww: lw, wh: entryHeight, scrollable: FALSE, parent: handle.outer, border: FALSE]]; wx _ wx + v.ww + entryHSpace}; Cmd: PROC [label: ROPE, proc: Buttons.ButtonProc] = { t: Buttons.Button _ Buttons.Create[ info: [ name: Rope.Concat[label, "!"], wx: wx, wy: wy, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: handle.outer, border: FALSE ], proc: proc, clientData: handle]; -- this will be passed to our button proc wx _ wx + t.ww + entryHSpace}; Bool: PROC [label: ROPE, initial: BOOL] RETURNS [flag: REF BOOL] = { t: Buttons.Button; br: REF BoolRec; flag _ NEW[BOOL _ initial]; br _ NEW[BoolRec _ [handle: handle, flag: flag]]; t _ Buttons.Create[ info: [ name: label, wx: wx, wy: wy, wh: entryHeight, -- specify rather than defaulting so line is uniform parent: handle.outer, border: TRUE ], proc: ToggleBool, clientData: br]; -- this will be passed to our button proc Buttons.SetDisplayStyle[ button: t, style: IF initial THEN $WhiteOnBlack ELSE $BlackOnWhite, paint: FALSE]; wx _ wx + t.ww + entryHSpace}; At[row: 0, x: entryHSpace]; Cmd["STOP", StopIt]; Cmd["run", RunProc]; Cmd["step", StepProc]; Cmd["zero", ZeroProc]; [] _ Label["cnt: "]; handle.cmd.iCount _ Value[NIL, 10]; handle.cmd.cacheLines _ NamedItem["lines", 3, "50"]; handle.cmd.cacheQuads _ NamedItem["q/l", 2, "2"]; handle.cmd.instCaches _ NamedItem["inst", 2, "2"]; handle.cmd.dataCaches _ NamedItem["data", 2, "0"]; NewLine[]; [] _ Label["stack: "]; stkX _ wx; handle.cmd.stk[0] _ Value[NIL, 10]; lclblX _ wx; [] _ Label["locals: "]; lclX _wx; handle.cmd.lcl[0] _ Value[NIL, 8]; tagX _ wx; [] _ Label["pc: "]; valX _ wx; handle.cmd.pc _ Value[NIL, 8]; nameX _ wx; handle.modelCache _ Bool["cache", TRUE]; Cmd["print", PrintCache]; NewLine[]; handle.show2Above _ Bool["+2", FALSE]; At[x: lclblX]; [] _ Label["spc"]; At[x: tagX]; [] _ Label["lf:"]; At[x: valX]; handle.cmd.lf _ Value[NIL, 8]; handle.cmd.lfName _ Value[NIL, 20]; Containers.ChildXBound[handle.outer, handle.cmd.lfName]; NewLine[]; handle.flushOnCall _ Bool["flsh", FALSE]; At[x: lclblX]; [] _ Label["ret"]; At[x: tagX]; [] _ Label["gf:"]; At[x: valX]; handle.cmd.gf _ Value[NIL, 8]; At[x: nameX]; handle.cmd.gfName _ Value[NIL, 20]; Containers.ChildXBound[handle.outer, handle.cmd.gfName]; NewLine[]; At[x: tagX]; [] _ Label["op:"]; At[x: valX]; handle.cmd.nextOp _ Value[NIL, 20]; Containers.ChildXBound[handle.outer, handle.cmd.nextOp]; NewLine[]; At[x: tagX]; Cmd["init", SetCommandLine]; handle.cmd.commandLine _ NamedItem["cmd", 20]; Containers.ChildXBound[handle.outer, handle.cmd.commandLine]; NewLine[]; At[x: tagX]; [] _ Label["Interpreted gfi's -"]; Cmd["clear", ClearGfiTable]; Cmd["print", PrintGfiTable]; NewLine[]; At[x: tagX]; Cmd["mark", MarkGfiInTable]; handle.cmd.gfiRange _ NamedItem["numbers", 20]; Containers.ChildXBound[handle.outer, handle.cmd.gfiRange]; NewLine[]; At[x: tagX]; handle.cmd.breakGF _ NamedItem["breakGF", 8, "0"]; trX _ wx; handle.traceOps _ Bool["tr", FALSE]; opsX _ wx; handle.countOps _ Bool["ops", FALSE]; xfrX _ wx; handle.recordXfers _ Bool["xfr", FALSE]; NewLine[]; At[x: tagX]; handle.cmd.breakPC _ NamedItem["breakPC", 8, "0"]; At[x: trX]; Cmd["tr", DumpTrace]; At[x: opsX]; Cmd["ops", DumpCounts]; At[x: xfrX]; Cmd["xfr", DumpXferData]; NewLine[]; At[x: tagX]; Cmd["showGfi", ShowGfiRange]; handle.cmd.configName _ NamedItem["of", 20]; Containers.ChildXBound[handle.outer, handle.cmd.configName]; NewLine[]; At[row: 5, x: lclblX]; [] _ Label["L0"]; FOR i: CARDINAL IN [1..PrincOps.stackDepth) DO At[row: i+1, x: stkX]; handle.cmd.stk[i] _ Value[NIL, 9]; At[x: lclX]; handle.cmd.lcl[i] _ Value[NIL, 8]; ENDLOOP; NewLine[]; END; Prompt: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN ph: PromptHandle _ NARROW[clientData]; SELECT mouseButton FROM blue, red => NULL; yellow => { r: ROPE = ViewerTools.GetSelectionContents[]; IF ph.number THEN [] _ Convert.IntFromRope[r ! Convert.Error => GO TO dont]; ViewerTools.SetContents[ph.viewer, r] EXITS dont => NULL; }; ENDCASE; ViewerTools.SetSelection[ph.viewer]; -- force the selection END; ToggleBool: Buttons.ButtonProc = TRUSTED { br: REF BoolRec _ NARROW [clientData]; switch: REF BOOL _ br.flag; switch^ _ ~switch^; Buttons.SetDisplayStyle[ button: NARROW[parent], style: IF switch^ THEN $WhiteOnBlack ELSE $BlackOnWhite]; UpdateDisplay[br.handle]; }; SetCommandLine: Buttons.ButtonProc = TRUSTED { h: Handle _ NARROW [clientData]; commandToolGfi: CARDINAL; gf: PrincOps.GlobalFrameHandle; gf _ PrincOpsUtils.GlobalFrame[LOOPHOLE[CommandTool.DoCommand]]; commandToolGfi _ gf.gfi; h.tsOut.PutText["Initializing to run command tool\n"]; Init[h, LOOPHOLE[CallCommandTool]]; -- we're going to push parameters DragomanPrivate.Push2[h.m, LOOPHOLE[h]]; MarkInteresting[h.m, commandToolGfi]; UpdateDisplay[h]}; CallCommandTool: PROC [handle: Handle] = { parent: Commander.Handle _ NEW[Commander.CommandObject _ [ in: IO.noInputStream, out: handle.tsOut, err: IO.noWhereStream]]; line: ROPE _ ViewerTools.GetContents[handle.cmd.commandLine]; prompt: ROPE _ "%l%% %l"; directory: ROPE _ "///Commands/"; IF Rope.Length[line] = 0 THEN RETURN; parent.propertyList _ List.PutAssoc[ key: $ErrorInputStream, val: IO.noInputStream, aList: parent.propertyList]; parent.propertyList _ List.PutAssoc[ key: $Prompt, val: prompt, aList: parent.propertyList]; parent.propertyList _ List.PutAssoc[ key: $SearchRules, val: LIST[directory], aList: parent.propertyList]; [] _ CommandTool.DoCommand[line, parent]; }; PrintCache: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN handle: Handle _ NARROW[clientData]; -- get our data IF ~EnterTool[handle] THEN RETURN; BEGIN ENABLE { UNWIND => ExitTool[handle]; ABORTED => {handle.tsOut.PutText[" aborted"]; GO TO done}}; DragomanPrivate.PrintCacheStats[handle.tsOut, handle.m]; handle.rc.print[handle.rc, handle.tsOut, "Real cache"]; handle.mc.print[handle.mc, handle.tsOut, "Map cache"]; ExitTool[handle]; EXITS done => ExitTool[handle]; END; END; ShowGfiRange: Buttons.ButtonProc = TRUSTED { h: Handle _ NARROW [clientData]; wc, cc: AMModel.Context; NoteMod: PROC [cx: AMModel.Context] RETURNS[stop: BOOL _ FALSE] = TRUSTED { SELECT AMModel.ContextClass[cx] FROM model => [] _ AMModel.ContextChildren[cx, NoteMod]; prog => { tx: TV = AMModelBridge.FrameFromContext[cx]; gf: PrincOps.GlobalFrameHandle = AMBridge.GFHFromTV[tx]; first, last: CARDINAL; [first, last] _ GfiRange[gf.gfi]; min _ MIN[min, first]; max _ MAX[max, last]}; ENDCASE; }; min: CARDINAL _ CARDINAL.LAST; max: CARDINAL _ CARDINAL.FIRST; config: ROPE _ ViewerTools.GetContents[h.cmd.configName]; wc _ AMModel.RootContext[WorldVM.LocalWorld[]]; IF config = NIL THEN RETURN; h.tsOut.PutRope[config]; cc _ AMModel.MostRecentNamedContext[config, wc]; IF cc = NIL THEN {h.tsOut.PutText[" not found\n"]; RETURN}; [] _ NoteMod[cc]; h.tsOut.PutF[" has minGfi: %g, and maxGfi: %g\n", IO.card[min], IO.card[max]]; }; GfiRange: PROC [gfi: PrincOps.GFTIndex] RETURNS [first, last: PrincOps.GFTIndex] = TRUSTED { gfItem: PrincOps.GFTItem _ PrincOps.GFT[gfi]; gf: PrincOps.GlobalFrameHandle; code: PrincOps.FrameCodeBase; cb: LONG POINTER TO PrincOps.CSegPrefix; gfItem.epbias _ 0; gf _ gfItem.framePtr; IF gf = NIL THEN RETURN [gfi, gfi]; first _ gf.gfi; code _ gf.code; code.out _ FALSE; cb _ code.longbase; last _ first + cb.header.info.ngfi - 1}; StopIt: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN handle: Handle _ NARROW[clientData]; -- get our data handle.m.singleStep _ TRUE; END; RunProc: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN handle: Handle _ NARROW[clientData]; -- get our data RunInContext: SAFE PROC = TRUSTED {DragomanPrivate.Execute[handle.m]}; IF ~EnterTool[handle] THEN RETURN; BEGIN ENABLE { UNWIND => ExitTool[handle]; FinishedExecution => {Finalize[handle]; GO TO done}; ABORTED => {handle.tsOut.PutText[" aborted"]; GO TO done}}; GetParamValues[handle]; handle.m.singleStep _ FALSE; NotifyRunning[handle]; handle.m.startOps _ handle.m.iCount; handle.m.startPulses _ BasicTime.GetClockPulses[]; ProcessProps.AddPropList[handle.workingDir, RunInContext]; PrintSpeed[handle]; ExitTool[handle]; EXITS done => ExitTool[handle]; END; END; PrintSpeed: PROC [h: Handle] = { et: REAL; et _ BasicTime.PulsesToSeconds[BasicTime.GetClockPulses[] - h.m.startPulses]; IO.PutF[h.tsOut, "executed %g instr in %g secs (%g/sec)\n", [cardinal[h.m.iCount - h.m.startOps]], [real[et]], [real[(h.m.iCount - h.m.startOps)/et]]]}; NotifyRunning: ENTRY PROC [h: Handle] = {NOTIFY h.running}; StepProc: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN handle: Handle _ NARROW[clientData]; -- get our data RunInContext: SAFE PROC = TRUSTED {DragomanPrivate.Execute[handle.m]}; IF ~EnterTool[handle] THEN RETURN; BEGIN ENABLE { UNWIND => ExitTool[handle]; FinishedExecution => {Finalize[handle]; GO TO done}; ABORTED => {handle.tsOut.PutText[" aborted"]; GO TO done}}; GetParamValues[handle]; handle.m.singleStep _ TRUE; ProcessProps.AddPropList[handle.workingDir, RunInContext]; ExitTool[handle]; EXITS done => ExitTool[handle]; END; END; ViewerValue: PROC [v: ViewerClasses.Viewer, default: INT] RETURNS [n: INT] = { n _ Convert.IntFromRope[ViewerTools.GetContents[v] ! SafeStorage.NarrowFault => {n _ default; GO TO gub}; Convert.Error => { MessageWindow.Append[message: "invalid number", clearFirst: TRUE]; n _ default; GO TO gub}; ]; EXITS gub => NULL; }; GetParamValues: PROC [h: Handle] = { gfc: CARDINAL; h.m.breakPC _ ViewerValue[h.cmd.breakPC, 0]; gfc _ ViewerValue[h.cmd.breakGF, 0]; h.m.breakGF _ LOOPHOLE[gfc]; h.m.countOps _ h.countOps^; h.m.traceOps _ h.traceOps^; DragomanPrivate.EnableCaches[m: h.m, state: h.modelCache^]; h.m.flushOnCall _ h.flushOnCall^; h.m.recordXferOut _ h.recordXfers^}; PrintGfiTable: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN h: Handle _ NARROW[clientData]; -- get our data igft: REF DragomanPrivate.BitVector; os: IO.STREAM; inRun, multiple: BOOL; first: BOOL _ TRUE; IF ~EnterTool[h] THEN RETURN; BEGIN ENABLE { UNWIND => ExitTool[h]; ABORTED => {h.tsOut.PutText[" aborted"]; GO TO done}}; igft _ h.m.interestingGfi; os _ h.tsOut; h.m.singleStep _ FALSE; -- so STOP button will stop printout inRun _ multiple _ FALSE; os.PutRope["\nInteresting gfi's: "]; FOR i: CARDINAL IN [1..PrincOps.GFTIndex.LAST+1] WHILE ~h.m.singleStep DO IF i <= PrincOps.GFTIndex.LAST AND igft[i] THEN IF inRun THEN multiple _ TRUE ELSE { IF first THEN first _ FALSE ELSE os.PutRope[", "]; os.PutF["%g", [cardinal[i]]]; inRun _ TRUE; multiple _ FALSE} ELSE { IF multiple THEN os.PutF["-%g", [cardinal[i-1]]]; inRun _ multiple _ FALSE}; ENDLOOP; os.PutRope["\n~~~~~~~~~~~~~~~\n"]; ExitTool[h]; EXITS done => ExitTool[h]; END; END; ClearGfiTable: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN handle: Handle _ NARROW[clientData]; -- get our data IF ~EnterTool[handle] THEN RETURN; BEGIN ENABLE { UNWIND => ExitTool[handle]; ABORTED => {handle.tsOut.PutText[" aborted"]; GO TO done}}; handle.m.interestingGfi^ _ ALL[FALSE]; ExitTool[handle]; EXITS done => ExitTool[handle]; END; END; MarkGfiByNumber: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN h: Handle _ NARROW[clientData]; -- get our data rangeRope, token: ROPE; gs: IO.STREAM; sepr: ROPE _ "Marking Gfis:"; NumberScan: IO.BreakProc = TRUSTED { RETURN [ SELECT char FROM IN ['0..'9] => other, ',, '- => break, ENDCASE => sepr]}; number, lower, upper: CARDINAL; IF ~EnterTool[h] THEN RETURN; BEGIN ENABLE { UNWIND => ExitTool[h]; ABORTED => {h.tsOut.PutText[" aborted"]; GO TO done}}; rangeRope _ ViewerTools.GetContents[h.cmd.gfiRange]; gs _ IO.RIS[rangeRope]; DO token _ IO.GetTokenRope[gs, NumberScan !IO.EndOfStream => EXIT].token; number _ Convert.IntFromRope[token ! SafeStorage.NarrowFault, Convert.Error => { MessageWindow.Append[message: "invalid number", clearFirst: TRUE]; EXIT}]; BEGIN -- find separator token _ IO.GetTokenRope[gs, NumberScan !IO.EndOfStream => GO TO single].token; SELECT Rope.Fetch[token, 0] FROM ', => GO TO single; '- => { [lower, ] _ GfiRange[number]; token _ IO.GetTokenRope[gs, NumberScan !IO.EndOfStream => EXIT].token; number _ Convert.IntFromRope[token ! SafeStorage.NarrowFault, Convert.Error => { MessageWindow.Append[message: "invalid number", clearFirst: TRUE]; EXIT}]; [, upper] _ GfiRange[number]; BEGIN -- eat comma token _ IO.GetTokenRope[gs, NumberScan !IO.EndOfStream => GO TO ok].token; IF Rope.Fetch[token, 0] # ', THEN { MessageWindow.Append[message: "missing comma", clearFirst: TRUE]; EXIT}; EXITS ok => NULL; END; }; ENDCASE => { MessageWindow.Append[message: "bad syntax", clearFirst: TRUE]; EXIT}; EXITS single => [lower, upper] _ GfiRange[number]; END; h.tsOut.PutF["%g %g", [rope[sepr]], [integer[lower]]]; sepr _ ","; IF upper # lower THEN h.tsOut.PutF["-%g", [integer[upper]]]; IF upper > PrincOps.GFTIndex.LAST THEN { MessageWindow.Append[message: "out of range", clearFirst: TRUE]; ExitTool[h]; RETURN}; FOR i: CARDINAL IN [lower..upper] DO h.m.interestingGfi[i] _ TRUE; ENDLOOP; ENDLOOP; h.tsOut.PutChar['\n]; ExitTool[h]; EXITS done => ExitTool[h]; END; END; MarkGFIByName: PROC [handle: Handle, name: ROPE] = { wc, cc: AMModel.Context; NoteMod: PROC [cx: AMModel.Context] RETURNS [stop: BOOL _ FALSE] = TRUSTED { SELECT AMModel.ContextClass[cx] FROM model => [] _ AMModel.ContextChildren[cx, NoteMod]; prog => { tx: AMTypes.TV = AMModelBridge.FrameFromContext[cx]; gf: PrincOps.GlobalFrameHandle = AMBridge.GFHFromTV[tx]; first, last: CARDINAL; [first, last] _ GfiRange[gf.gfi]; min _ MIN[min, first]; max _ MAX[max, last]}; ENDCASE; }; min: CARDINAL _ CARDINAL.LAST; max: CARDINAL _ CARDINAL.FIRST; TRUSTED {wc _ AMModel.RootContext[WorldVM.LocalWorld[]]}; IF name = NIL THEN RETURN; TRUSTED {cc _ AMModel.MostRecentNamedContext[name, wc]}; IF cc = NIL THEN {Output[handle, "*** MarkGFI: ", name, " not found\n"]; RETURN}; [] _ NoteMod[cc]; Output[handle, "MarkGFI: ", name, " gfi's are marked (", Convert.RopeFromInt[min], "-", Convert.RopeFromInt[max], ")\n"]; FOR i: CARDINAL IN [min..max] DO handle.m.interestingGfi[i] _ TRUE; ENDLOOP; }; ZeroProc: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN handle: Handle _ NARROW[clientData]; -- get our data IF ~EnterTool[handle] THEN RETURN; BEGIN ENABLE { UNWIND => ExitTool[handle]; ABORTED => {handle.tsOut.PutText[" aborted"]; GO TO done}}; handle.m.iCount _ 0; SetValue[handle.cmd.iCount, "0"]; ExitTool[handle]; EXITS done => ExitTool[handle]; END; END; EnterTool: ENTRY PROC [h: Handle] RETURNS [BOOL] = { IF h.busy THEN { MessageWindow.Append[message: "Already running", clearFirst: TRUE]; RETURN[FALSE]}; IF h.m = NIL THEN { MessageWindow.Append[message: "Please initialize", clearFirst: TRUE]; RETURN[FALSE]}; h.busy _ TRUE; RETURN[TRUE]; }; ExitTool: ENTRY PROC [h: Handle] = {UpdateDisplay[h]; h.busy _ FALSE}; Finalize: ENTRY PROC [h: Handle] = { FinalizeInternal[h]; }; FinalizeInternal: INTERNAL PROC [h: Handle] = { h.ready _ FALSE; PrintSpeed[h]; h.killCountProcess _ TRUE; NOTIFY h.running; }; Initialize: PUBLIC PROC [handle: REF ANY, topProc: PROC] = { h: Handle = NARROW[handle]; Init[h, topProc]}; Init: ENTRY PROC [h: Handle, topProc: PROC] = TRUSTED { link: PrincOps.ControlLink = LOOPHOLE[topProc]; IF h.busy THEN { MessageWindow.Append[message: "Stop before reinitializing", clearFirst: TRUE]; RETURN}; IF h.ready THEN FinalizeInternal[h]; ResetMachine[h.m]; h.m.opCount^ _ ALL[0]; h.m.history.head _ h.m.history.tail _ 0; MarkInteresting[h.m, link.gfi]; DragomanPrivate.Xfer[ m: h.m, dst: link, src: DragomanPrivate.MagicReturn, push: FALSE]; h.ready _ TRUE; h.busy _ FALSE; UpdateDisplay[h]; h.killCountProcess _ FALSE; Process.Detach[FORK ShowCount[h]]; }; MarkInteresting: PROC [m: Machine, gfi: PrincOps.GFTIndex] = { first, last: CARDINAL; [first, last] _ GfiRange[gfi]; FOR i: CARDINAL IN [first..last] DO m.interestingGfi[i] _ TRUE; ENDLOOP; }; ResetMachine: PROC [m: Machine] = { m.sd _ 0; m.stack _ ALL[[0]]; m.l _ NIL; m.g _ NIL; m.cb _ NIL; m.pc _ 0; m.iCount _ 0; m.outCalls _ 0; m.singleStep _ FALSE; m.breakGF _ NIL; m.breakPC _ 0; m.traceOps _ FALSE; m.recordXferOut _ FALSE; m.xferData _ NIL; m.countOps _ FALSE; m.flushOnCall _ FALSE; m.startPulses _ 0; m.startOps _ 0}; MarkGfiInTable: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN h: Handle _ NARROW[clientData]; -- get our data rangeRope, token: ROPE; gs: IO.STREAM; sepr: ROPE _ "Marking Gfis:"; TokenScan: IO.BreakProc = TRUSTED { RETURN [ SELECT char FROM IN ['0..'9], IN ['A..'Z], IN ['a..'z] => other, ',, '- => break, ENDCASE => sepr]}; number, lower, upper: CARDINAL; IF ~EnterTool[h] THEN RETURN; BEGIN ENABLE { UNWIND => ExitTool[h]; ABORTED => {h.tsOut.PutText[" aborted"]; GO TO done}}; rangeRope _ ViewerTools.GetContents[h.cmd.gfiRange]; gs _ IO.RIS[rangeRope]; DO token _ IO.GetTokenRope[gs, TokenScan !IO.EndOfStream => EXIT].token; SELECT Rope.Fetch[token, 0] FROM IN ['0..'9] => number _ Convert.IntFromRope[token ! SafeStorage.NarrowFault, Convert.Error => { MessageWindow.Append[message: "invalid number", clearFirst: TRUE]; EXIT}]; IN ['A..'Z], IN ['a..'z] => { MarkGFIByName[h, token]; token _ IO.GetTokenRope[gs, TokenScan !IO.EndOfStream => EXIT].token; IF Rope.Fetch[token, 0] # ', THEN { MessageWindow.Append[message: "bad syntax", clearFirst: TRUE]; EXIT}; LOOP}; ENDCASE; BEGIN -- find separator token _ IO.GetTokenRope[gs, TokenScan !IO.EndOfStream => GO TO single].token; SELECT Rope.Fetch[token, 0] FROM ', => GO TO single; '- => { [lower, ] _ GfiRange[number]; token _ IO.GetTokenRope[gs, TokenScan !IO.EndOfStream => EXIT].token; number _ Convert.IntFromRope[token ! SafeStorage.NarrowFault, Convert.Error => { MessageWindow.Append[message: "invalid number", clearFirst: TRUE]; EXIT}]; [, upper] _ GfiRange[number]; BEGIN -- eat comma token _ IO.GetTokenRope[gs, TokenScan !IO.EndOfStream => GO TO ok].token; IF Rope.Fetch[token, 0] # ', THEN { MessageWindow.Append[message: "missing comma", clearFirst: TRUE]; EXIT}; EXITS ok => NULL; END; }; ENDCASE => { MessageWindow.Append[message: "bad syntax", clearFirst: TRUE]; EXIT}; EXITS single => [lower, upper] _ GfiRange[number]; END; h.tsOut.PutF["%g %g", [rope[sepr]], [integer[lower]]]; sepr _ ","; IF upper # lower THEN h.tsOut.PutF["-%g", [integer[upper]]]; IF upper > PrincOps.GFTIndex.LAST THEN { MessageWindow.Append[message: "out of range", clearFirst: TRUE]; ExitTool[h]; RETURN}; FOR i: CARDINAL IN [lower..upper] DO h.m.interestingGfi[i] _ TRUE; ENDLOOP; ENDLOOP; h.tsOut.PutChar['\n]; ExitTool[h]; EXITS done => ExitTool[h]; END; END; ShowCount: ENTRY PROC [h: Handle] = { m: Machine = h.m; IF m = NIL THEN RETURN; TRUSTED {Process.InitializeCondition[@h.pauseTime, Process.MsecToTicks[1000]]}; WHILE ~h.killCountProcess DO WHILE (~h.busy OR h.m.singleStep) DO WAIT h.running; IF h.killCountProcess THEN RETURN; ENDLOOP; WAIT h.pauseTime; SetValue[h.cmd.iCount, Convert.RopeFromInt[m.iCount]]; ENDLOOP; }; UpdateDisplay: PROC [handle: Handle] = { m: Machine = handle.m; IF m = NIL THEN RETURN; SetValue[handle.cmd.iCount, Convert.RopeFromInt[m.iCount]]; SetValue[handle.cmd.pc, Convert.RopeFromInt[m.pc, 8]]; DisplayStack[handle]; SetValue[handle.cmd.lf, Convert.RopeFromInt[LOOPHOLE[m.l, CARDINAL], 8]]; DisplayLfName[handle]; SetValue[handle.cmd.gf, Convert.RopeFromInt[LOOPHOLE[m.g, CARDINAL], 8]]; DisplayGfName[handle]; DisplayLocals[handle]; DisplayNextOp[handle]; }; DisplayLfName: PROC [handle: Handle] = TRUSTED { m: Machine = handle.m; name: ROPE; BEGIN ENABLE AMTypes.Error, SafeStorage.NarrowFault => GO TO forgetIt; ftv: TV _ AMBridge.TVForFrame[fh: LOOPHOLE[m.l]]; ptv: TV _ AMTypes.Procedure[ftv]; name _ AMTypes.TVToName[ptv]; EXITS forgetIt => name _ NIL; END; SetValue[handle.cmd.lfName, name]}; DisplayGfName: PROC [handle: Handle] = TRUSTED { m: Machine = handle.m; name: ROPE; BEGIN ENABLE AMTypes.Error, SafeStorage.NarrowFault => GO TO forgetIt; ftv: TV _ AMBridge.TVForGFHReferent[LOOPHOLE[m.g]]; name _ AMTypes.TVToName[ftv]; EXITS forgetIt => name _ NIL; END; SetValue[handle.cmd.gfName, name]}; DisplayStack: PROC [handle: Handle] = { m: Machine = handle.m; i: CARDINAL; FOR i _ 0, i+1 WHILE i < PrincOps.stackDepth AND i < m.sd DO l: ViewerClasses.Viewer = handle.cmd.stk[i]; SetValue[l, Convert.RopeFromInt[m.stack[i], 8]]; ENDLOOP; IF handle.show2Above^ THEN WHILE i < MIN[m.sd+2, PrincOps.stackDepth] DO l: ViewerClasses.Viewer = handle.cmd.stk[i]; SetValue[l, Rope.Cat["<", Convert.RopeFromInt[m.stack[i], 8], ">"]]; i _ i + 1; ENDLOOP; WHILE i < PrincOps.stackDepth DO l: ViewerClasses.Viewer = handle.cmd.stk[i]; SetValue[l, NIL]; i _ i + 1; ENDLOOP; }; DisplayLocals: PROC [handle: Handle] = TRUSTED { m: Machine = handle.m; maxShown: CARDINAL = PrincOps.stackDepth; lSize: CARDINAL; i: CARDINAL; fsi: CARDINAL; local: POINTER TO ARRAY [0..maxShown) OF CARDINAL = LOOPHOLE[m.l]; IF local = NIL OR (LOOPHOLE[local, CARDINAL] MOD 4 # 0) THEN RETURN; fsi _ LOOPHOLE[local-1, POINTER TO CARDINAL]^; lSize _ IF fsi IN PrincOps.FrameSizeIndex THEN PrincOps.FrameVec[fsi] ELSE 0; FOR i _ 0, i+1 WHILE i < maxShown AND i < lSize DO l: Labels.Label = handle.cmd.lcl[i]; SetValue[l, Convert.RopeFromInt[local[i], 8]]; ENDLOOP; WHILE i < maxShown DO l: Labels.Label = handle.cmd.lcl[i]; SetValue[l, NIL]; i _ i + 1; ENDLOOP; }; JumpOp: TYPE = [PrincOps.zJ2..PrincOps.zJIW]; DisplayNextOp: PROC [handle: Handle] = TRUSTED { m: Machine = handle.m; opData: ListerUtils.OpCodeArray _ DragomanOpDebug.OpData[]; cb: LONG POINTER TO PACKED ARRAY [0..0) OF Byte = m.cb; op: ARRAY [0..3) OF Byte _ [cb[m.pc], 0, 0]; IF opData # NIL THEN SELECT opData[op[0]].length FROM 2 => op[1] _ cb[m.pc+1]; 3 => {op[2] _ cb[m.pc+2]; op[1] _ cb[m.pc+1]}; ENDCASE; SetValue[handle.cmd.nextOp, DragomanOpDebug.RopeForOperation[op, m.pc]]; }; Output: PUBLIC PROC [handle: Handle, r1, r2, r3, r4, r5, r6, r7, r8: ROPE _ NIL] = { PR: PROC [r: ROPE] = INLINE {IF r # NIL THEN IO.PutRope[handle.tsOut, r]}; PR[r1]; PR[r2]; PR[r3]; PR[r4]; PR[r5]; PR[r6]; PR[r7]; PR[r8]}; DumpCounts: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN h: Handle _ NARROW[clientData]; -- get our data IF h.m.countOps THEN DragomanPrivate.PrintOpCounts[h.tsOut, h.m] ELSE h.tsOut.PutRope["\nOpcodes not being counted\n"]; END; DumpTrace: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN handle: Handle _ NARROW[clientData]; -- get our data PrintHistory[handle.tsOut, handle, TRUE]; UpdateDisplay[handle]; END; DumpXferData: Buttons.ButtonProc -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- = TRUSTED BEGIN h: Handle _ NARROW[clientData]; -- get our data IF h.m.recordXferOut THEN DragomanPrivate.PrintProcCounts[h.tsOut, h.m] ELSE h.tsOut.PutRope["\nXfers not being counted\n"]; END; PrintHistory: PUBLIC PROC [os: IO.STREAM, h: Handle, modNames: BOOL] = { history: DragomanPrivate.OpHistory _ h.m.history; Octal: PROC [n: INT] RETURNS [IO.Value] = { RETURN [[rope[Convert.RopeFromInt[n, 8, n>7]]]]}; ShortOctal: PROC [n: CARDINAL] RETURNS [IO.Value] = { RETURN [[rope[Convert.RopeFromInt[n, 8, n>7]]]]}; PrintItem: PROC [item: DragomanPrivate.OpHistoryItem] = TRUSTED { cgf: CARDINAL _ LOOPHOLE[item.gf]; os.PutF["%6g %6g %g", Octal[cgf], Octal[item.pc], [rope[DragomanOpDebug.RopeForOperation[item.op, item.pc]]]]; IF item.stkDepth # 0 THEN { os.PutF["\t%g", ShortOctal[item.stk[0]]]; FOR j: CARDINAL IN [1..item.stkDepth) DO os.PutF[", %g", ShortOctal[item.stk[j]]]; ENDLOOP; }; IF modNames THEN { ENABLE AMTypes.Error, SafeStorage.NarrowFault => GO TO forgetIt; ftv: AMTypes.TV _ AMBridge.TVForGFHReferent[item.gf]; name: ROPE _ AMTypes.TVToName[ftv]; os.PutF["\t%g\n", [rope[name]]] EXITS forgetIt => NULL; } ELSE os.PutChar['\n]}; os.PutRope["\n~~~~~~~~~~~~~~~~~~~~~~~~~\n"]; FOR i: CARDINAL _ history.head, (i+1) MOD DragomanPrivate.OpHistorySize WHILE i # history.tail DO PrintItem[history.data[i]]; ENDLOOP; RETURN}; Commander.Register[key: "DragomanDebug", proc: MakeTool, doc: "Create a window interface for Dragoman" ]; END. dDragomanDebug.mesa; Last Edited by: Sweet, November 18, 1985 10:54:37 am PST The Containers interface is used to create an outer envelope or "container" for the different sections below. For uniformity, we define some standard distances between entries in the tool. default the width so that it will be computed for us -- don't use Label, since the text can't be selected. default the width so that it will be computed for us -- default the width so that it will be computed for us -- force the selection into the user input field SetInitialProc: Buttons.ButtonProc = TRUSTED { h: Handle _ NARROW [clientData]; ptv: TV; ptype: AMTypes.Type; noVal: BOOL; cmd, error: ROPE; proc: PROC; IF h.m # NIL THEN GetParamValues[h]; cmd _ ViewerTools.GetContents[h.cmd.initialProc]; IF cmd = NIL THEN { MessageWindow.Append[message: "Enter initial proc name", clearFirst: TRUE]; RETURN}; [result: ptv, errorRope: error, noResult: noVal] _ Interpreter.Evaluate[cmd]; IF error # NIL THEN { MessageWindow.Append[message: "Evaluation problem: ", clearFirst: TRUE]; MessageWindow.Append[message: error, clearFirst: FALSE]; RETURN}; ptype _ AMTypes.TVType[ptv]; IF AMTypes.TypeClass[ptype] # procedure THEN { MessageWindow.Append[message: "Not a procedure", clearFirst: TRUE]; RETURN}; IF AMTypes.Domain[ptype] # SafeStorage.nullType OR AMTypes.Range[ptype] # SafeStorage.nullType THEN { MessageWindow.Append[message: "Must be parameterless", clearFirst: TRUE]; RETURN}; proc _ NARROW[AMBridge.TVToProc[ptv]]; h.tsOut.PutText["Initializing to "]; h.tsOut.PutRope[cmd]; h.tsOut.PutText["\n"]; Init[h, proc]; }; lines, quads, instr, data: INT; Ê-t˜Jšœ™Icode™8K™šÏ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šœ œS˜dKšœ ˜ Kš œ œœœœ˜=Kšœµœž˜Þ—Kš˜Kšœ½™½Kšœ œÏc&˜BKšœ œž'˜CKšœ œž+˜GKšœœœ˜Kšœœœœ˜Kšœœ œ˜Kšœœ˜Kšœ œ˜(K˜K˜Kšœ œœ˜(K˜KšœœœžÐckž]˜šœœœž+˜KKšœœž%˜HKšœœž0˜BKšœž˜$Kšœ œœ˜KšœFœœ˜OKšœ œ˜)Kšœ œ˜K˜Kšœ˜Kšœ˜K˜Kšœ œ˜Kšœž˜.K˜—šœœœ˜Kšœ˜KšœE˜EKšœ˜Kšœ!˜!Kšœ!˜!Kšœ˜Kšœœœ˜Kšœž ˜2Kšœœž4˜CKšœž˜.Kšœ œœž'˜=—Kšœž!˜3KšœP˜PKšœ'˜'Kšœ)ž˜=Kšœ˜Kšœ>ž ˜KJšœQž ˜]Jšœ œ;˜HKšœaœ%˜‹Kšœaœ%˜‹šœœ$˜.Kšœ˜Kšœ œ ˜,Kš œ œœœœ˜!Kšœœœœ˜?—Kšœ&ž˜=Kšœ˜—K˜šÏnœœœ˜.Kšœ-ž(˜Ušœ˜KšœMœ˜V—šœ<˜—Kšœ˜—š œœ œ œœœœ˜DK˜Kšœœ ˜Kšœœœ ˜Kšœœ)˜1šœ˜šœ˜Kšœ ˜ K˜Kšœ˜Kšœ7™7Kšœž4˜EKšœ˜Kšœœ˜—Kšœ˜Kšœž(œ˜:—K•StartOfExpansion[]š œ,œ œœœ˜lKšœ˜—K˜K˜0K˜BKšœ/œ˜8Kšœ4˜4K˜1K˜2K˜2K˜K˜K˜ K˜K˜"Kšœœ˜0K˜!Kšœœ˜-K˜Kšœœ˜Kšœ.œ˜5Kšœ˜Kšœ ˜ K˜Kšœœ˜&K˜"KšœCœ˜KKšœœ˜#Kšœ8˜8K˜ K˜Kšœ"œ˜)K˜AKšœ#œ˜+Kšœ(œ˜1Kšœ8˜8K˜ K˜KšœGœ˜PKšœ8˜8K˜ K˜K˜)Kšœ.˜.Kšœ=˜=K˜ K˜K˜/K˜9K˜ K˜)Kšœ/˜/Kšœ:˜:K˜ K˜?Kšœ'œ˜.Kšœ)œ˜0Kšœ,œ˜3K˜ K˜K˜K˜?K˜!Kšœ$˜$Kšœ&˜&K˜ K˜K˜K˜*Kšœ,˜,Kšœ<˜Kšœ œ˜K˜Kš œœœœœœ˜HK˜K˜—š  œœ˜#Jšœ ˜ Jšœ œ˜Jšœœ˜ Jšœœ˜ Jšœœ˜ Jšœ ˜ Kšœ˜Kšœ˜Kšœœ˜Kšœ œ˜Kšœ˜Kšœ œ˜Kšœœ˜Kšœ œ˜Kšœ œ˜Kšœœ˜Jšœ˜Jšœ˜K˜K˜K˜—–† -- [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] -- š œŸ…œ ˜¹Kšœ œž˜/Kšœœ˜Kšœœœ˜Kšœœ˜šœ œ œ˜#šœœ˜Kšœ œ œ˜/Kšœ˜Kšœ ˜——Kšœœ˜Kšœœœ˜Kš˜šœ˜Kšœ˜Kšœ"œœ˜6—Kšœ4˜4Kšœœœ ˜š˜Kšœœœœ˜Ešœ˜ šœ2˜4šœ+˜+Jšœ<œ˜BJšœ˜——šœ œ˜Jšœ˜Jšœœœœ˜Ešœœ˜#Jšœ8œœ˜E—Jšœ˜—Jšœ˜—Jšœž˜Jš œœœœœ˜Mšœ˜ Jšœœœ˜˜J˜Jšœœœœ˜Ešœ%˜%šœ+˜+Jšœ<œ˜BJšœ˜——J˜Jšœž ˜Jš œœœœœ ˜Išœœ˜#Jšœ;œ˜AJšœ˜—š˜Jšœœ˜ —Jšœ˜J˜—šœ˜ Jšœ8œœ˜E——š˜J˜,—Jšœ˜J˜BJšœœ'˜<šœœœ˜(Kšœ:œ˜@Kšœ œ˜—šœœœ˜$Kšœœ˜Kšœ˜—Jšœ˜—J˜K˜ š˜Kšœ˜—Kšœ˜Kšœ˜K˜—š  œœœ˜%K˜Kšœœœœ˜KšœH˜Ošœ˜šœ œ˜$Kšœ ˜Kšœœœ˜"Kšœ˜—Kšœ ˜Kšœ6˜6Kšœ˜—K˜—K˜š  œœ˜(K˜Kšœœœœ˜Kšœ;˜;Kšœ6˜6K˜Kšœ,œœ˜IK˜Kšœ,œœ˜IK˜K˜K˜K˜K˜—š  œœœ˜0K˜Kšœœ˜ Kšœœ+œœ ˜FKšœœœ˜1Kšœœ˜!K˜š˜Kšœœ˜—Kšœ˜Kšœ#˜#K˜—š  œœœ˜0K˜Kšœœ˜ Kšœœ+œœ ˜FKšœœœ˜3K˜š˜Kšœœ˜—Kšœ˜Kšœ#˜#K˜—š  œœ˜'K˜Kšœœ˜ šœ œœ ˜