-- File: CountCommands.mesa -- Last edited by Sandman on October 21, 1980 3:00 PM DIRECTORY Ascii USING [CR, SP], CountDefs USING [ condition, groups, loadmatrix, mode, modePos, module, mon, monitorOn, printmodule, printsorted, printtables, process, processPos, setProcess, sortClass, window, zero], CountPrivate USING [ ControlRecord, GroupHandle, GroupIndex, MaxGroup, Mode, PCR, TableHandle, VersionID], DebugUsefulDefs USING [ Frame, GFHandle, Name, ShortCopyREAD, ShortCopyWRITE, ShortREAD, ShortWRITE], Event USING [AddNotifier, Item, Masks, Notifier], Format USING [NumberFormat], FormSW USING [DisplayItem, EnumeratedNotifyProcType, NotifyProcType], Gf USING [GFI, Handle, Validate], Lookup USING [Fail], MachineDefs USING [GFHandle, GfiToFrame, GfiToOffset, NullGF], MsgSW USING [Clear, Post], PerfCommonOps USING [ cmdSW, ConditionBreaks, logSW, msgSW, Number, WriteConvertedTicksToMs, WriteLongNumber, WritePercent, Zero], PerfStructures USING [IndexToHandle, NullPsbHandle, PsbIndex], Put USING [Blanks, Char, CR, Decimal, Line, Number, Text], SDDefs USING [sBreak, SD, sXferTrapMonitor], Selection USING [Convert, Number], Storage USING [FreeString, FreeWords, Words], Streams USING [Destroy, End, GetByte, Handle, NewStream], String USING [ AppendChar, AppendOctal, AppendString, InvalidNumber, StringToNumber], Time USING [Append, Current, Unpack], ToolWindow USING [GetName], UserInput USING [userAbort]; CountCommands: PROGRAM IMPORTS CountDefs, DebugUsefulDefs, Event, FormSW, Gf, Lookup, MachineDefs, MsgSW, PerfCommonOps, Put, Selection, Streams, String, Storage, Time, ToolWindow, UserInput, PerfStructures EXPORTS CountDefs = PUBLIC BEGIN OPEN CountDefs, CountPrivate, DebugUsefulDefs, PerfCommonOps; cr: ControlRecord; counts, times: TableHandle; groups: PRIVATE GroupHandle; counterOn: BOOLEAN _ FALSE; haveTable, dirtyTable: PACKED ARRAY {counts, times, cr, groups} OF BOOLEAN _ [FALSE, FALSE, FALSE, FALSE]; GetCounts: PROCEDURE [mode: {read, write}] RETURNS [TableHandle] = BEGIN OPEN DebugUsefulDefs; IF ~haveTable[counts] THEN BEGIN length: CARDINAL; [] _ GetCR[read]; length _ cr.length*SIZE[LONG CARDINAL]; counts _ Storage.Words[length]; ShortCopyREAD[to: counts, from: cr.counts, nwords: length]; haveTable[counts] _ TRUE; END; dirtyTable[counts] _ dirtyTable[counts] OR mode = write; RETURN[counts]; END; GetTimes: PROCEDURE [mode: {read, write}] RETURNS [TableHandle] = BEGIN OPEN DebugUsefulDefs; IF ~haveTable[times] THEN BEGIN length: CARDINAL; [] _ GetCR[read]; length _ cr.length*SIZE[LONG CARDINAL]; times _ Storage.Words[length]; ShortCopyREAD[to: times, from: cr.times, nwords: length]; haveTable[times] _ TRUE; END; dirtyTable[times] _ dirtyTable[times] OR mode = write; RETURN[times]; END; GetCR: PROCEDURE [mode: {read, write}] RETURNS [POINTER TO ControlRecord] = BEGIN OPEN DebugUsefulDefs; IF ~haveTable[cr] THEN BEGIN rec: PCR _ ShortREAD[@SDDefs.SD[SDDefs.sXferTrapMonitor]]; ShortCopyREAD[to: @cr, from: rec, nwords: SIZE[ControlRecord]]; haveTable[cr] _ TRUE; END; dirtyTable[cr] _ dirtyTable[cr] OR mode = write; RETURN[@cr]; END; GetGroups: PROCEDURE [mode: {read, write}] RETURNS [GroupHandle] = BEGIN OPEN DebugUsefulDefs; IF ~haveTable[groups] THEN BEGIN length: CARDINAL; [] _ GetCR[read]; length _ cr.length*SIZE[GroupIndex]; groups _ Storage.Words[length]; ShortCopyREAD[to: groups, from: cr.groups, nwords: length]; haveTable[groups] _ TRUE; END; dirtyTable[groups] _ dirtyTable[groups] OR mode = write; RETURN[groups]; END; TablesExist: PROCEDURE RETURNS [BOOLEAN] = BEGIN RETURN[DebugUsefulDefs.ShortREAD[@SDDefs.SD[SDDefs.sXferTrapMonitor]] # NIL]; END; TablesCorrectVersion: PROCEDURE RETURNS [BOOLEAN] = BEGIN RETURN[GetCR[read].version = VersionID]; END; PutTables: PUBLIC PROCEDURE [flush: BOOLEAN] = BEGIN OPEN DebugUsefulDefs; rec: PCR _ ShortREAD[@SDDefs.SD[SDDefs.sXferTrapMonitor]]; length: CARDINAL = cr.length*SIZE[LONG CARDINAL]; IF flush AND haveTable[cr] AND dirtyTable[cr] THEN ShortCopyWRITE[to: rec, from: @cr, nwords: SIZE[ControlRecord]]; IF haveTable[counts] THEN BEGIN IF flush AND dirtyTable[counts] THEN ShortCopyWRITE[from: counts, to: cr.counts, nwords: length]; Storage.FreeWords[counts]; END; IF haveTable[times] THEN BEGIN IF flush AND dirtyTable[times] THEN ShortCopyWRITE[from: times, to: cr.times, nwords: length]; Storage.FreeWords[times]; END; IF haveTable[groups] THEN BEGIN IF flush AND dirtyTable[groups] THEN ShortCopyWRITE[from: groups, to: cr.groups, nwords: cr.length*SIZE[GroupIndex]]; Storage.FreeWords[groups]; END; haveTable _ dirtyTable _ [FALSE, FALSE, FALSE, FALSE]; END; TurnOn: PROCEDURE = BEGIN OPEN SDDefs; cr.saveBreakHandler _ DebugUsefulDefs.ShortREAD[@SD[sBreak]]; DebugUsefulDefs.ShortWRITE[@SD[sBreak], cr.self]; END; TurnOff: PROCEDURE RETURNS [BOOLEAN] = -- this procedure turns off the measurements BEGIN OPEN SDDefs; IF cr.saveBreakHandler # NIL AND cr.self = DebugUsefulDefs.ShortREAD[ @SD[sBreak]] THEN BEGIN DebugUsefulDefs.ShortWRITE[@SD[sBreak], cr.saveBreakHandler]; RETURN[TRUE] END; RETURN[FALSE]; END; ZeroCounts: PROCEDURE = BEGIN table: TableHandle _ GetCounts[write]; length: CARDINAL = GetCR[read].length*SIZE[LONG CARDINAL]; PerfCommonOps.Zero[table, length]; table _ GetTimes[write]; PerfCommonOps.Zero[table, length]; GetCR[write].gfi _ 0; Put.Line[logSW, "Tables zeroed"L]; GetCR[write].newMeasurement _ TRUE; RETURN END; SetProcess: PROCEDURE = BEGIN OPEN PerfStructures; pCR: PCR _ GetCR[write]; index: PsbIndex; IF process = NIL OR process.length = 0 THEN BEGIN pCR.process _ NullPsbHandle; Put.Line[logSW, "All processes now tracked"L]; END ELSE BEGIN index _ String.StringToNumber[ process, 8 ! String.InvalidNumber => GOTO badProcess]; Put.Text[logSW, "Track process: "L]; Put.Line[logSW, process]; pCR.process _ IndexToHandle[index]; EXITS badProcess => PostError[badProcess]; END; END; PrintTables: PROCEDURE = BEGIN sort: Sorting _ GetSorting[]; PrintSorted[sort ! UNWIND => ReleaseSorting[sort]]; ReleaseSorting[sort]; END; DisplaySorted: PROCEDURE = BEGIN sort: Sorting; IF mode = matrix THEN {PostError[badMode]; RETURN}; sort _ GetSorting[]; SortTable[ table: IF sortClass = count THEN GetCounts[read] ELSE GetTimes[read], sorting: sort]; PrintSorted[sort ! UNWIND => ReleaseSorting[sort]]; ReleaseSorting[sort]; END; DisplayModule: PROCEDURE = BEGIN gfi: CARDINAL; IF mode = matrix THEN {PostError[badMode]; RETURN}; IF module = NIL OR module.length = 0 THEN BEGIN PostError[badModule]; RETURN END; IF module[0] IN ['0..'9] THEN gfi _ String.StringToNumber[module, 8] ELSE gfi _ LOOPHOLE[DebugUsefulDefs.Frame[module]]; IF gfi >= GetCR[read].length THEN gfi _ Gf.GFI[LOOPHOLE[gfi]]; Put.CR[logSW]; DisplayLine[ gfi, GetCount[gfi], TotalCounts[plain], GetTime[gfi], TotalTimes[plain]]; Put.CR[logSW]; RETURN END; PrintSorted: PROCEDURE [sort: Sorting] = BEGIN i, gfi: CARDINAL; count, time, totalCounts, totalTime: Number; [totalCounts, totalTime] _ PrintHeader[]; FOR i IN [0..GetCR[read].length) DO IF (gfi _ sort[i]) = 0 THEN LOOP; IF MachineDefs.GfiToOffset[gfi] # 0 THEN LOOP; count _ GetCount[gfi]; IF count = 0 THEN LOOP; Put.CR[logSW]; time _ GetTime[gfi]; DisplayLine[gfi, count, totalCounts, time, totalTime]; ENDLOOP; Put.CR[logSW]; PrintTrailer[plain]; Put.CR[logSW]; END; DisplayLine: PROCEDURE [ gfi: CARDINAL, count, totalCounts, time, totalTime: Number] = BEGIN f: Format.NumberFormat = [base: 8, unsigned: TRUE, zerofill: FALSE, columns: 7]; g: Format.NumberFormat = [base: 8, unsigned: TRUE, zerofill: FALSE, columns: 3]; module: STRING _ [40]; frame: MachineDefs.GFHandle; IF UserInput.userAbort THEN SIGNAL Aborted; frame _ MachineDefs.GfiToFrame[gfi]; CheckFrame[frame ! Aborted => GOTO bailout]; Put.Number[logSW, gfi, g]; Put.Char[logSW, 'B]; Put.Number[logSW, frame, f]; Put.Text[logSW, "B "L]; WriteFrameName[frame, 18]; WriteLongNumber[count, 12]; WritePercent[count, totalCounts, 8]; WriteConvertedTicksToMs[time, GetCR[read].pulseConversion, 12]; WritePercent[time, totalTime, 7]; RETURN; EXITS bailout => RETURN END; CheckFrame: PROCEDURE [frame: MachineDefs.GFHandle] = BEGIN s: STRING _ [24]; IF frame # NIL AND Gf.Validate[frame] THEN RETURN; String.AppendString[s, "! Invalid frame "L]; String.AppendOctal[s, frame]; MsgSW.Post[sw: msgSW, string: s, endOfMsg: TRUE]; Put.Text[logSW, s]; SIGNAL Aborted; RETURN END; Aborted: SIGNAL = CODE; PrintTotals: PROCEDURE [mode: Mode] RETURNS [totalCounts, totalTime: Number] = BEGIN Put.CR[logSW]; totalCounts _ TotalCounts[mode]; totalTime _ TotalTimes[mode]; Put.Text[logSW, "Total Xfers"L]; WriteLongNumber[totalCounts, 14]; Put.CR[logSW]; Put.Text[logSW, "Total Time "L]; WriteConvertedTicksToMs[totalTime, GetCR[read].pulseConversion, 14]; END; PrintHeader: PROCEDURE RETURNS [totalCounts, totalTime: Number] = BEGIN [totalCounts: totalCounts, totalTime: totalTime] _ PrintTotals[plain]; Put.Text[ logSW, " Gfi Frame Module #Xfers %Xfers Time %Time ---- ------- ------------------ ----------- ------- ----------- ------"L]; END; PrintTrailer: PROCEDURE [mode: Mode] = BEGIN Put.Text[logSW, "Ignored Xfers"L]; WriteLongNumber[Ignored[GetCounts[read], mode], 14]; Put.CR[logSW]; Put.Text[logSW, "Ignored Time "L]; WriteConvertedTicksToMs[ Ignored[GetTimes[read], mode], GetCR[read].pulseConversion, 14]; END; WriteFrameName: PROCEDURE [frame: MachineDefs.GFHandle, columns: CARDINAL _ 0] = BEGIN module: STRING _ [40]; DebugUsefulDefs.Name[gf: frame, name: module]; PrintString[module, columns]; RETURN; END; TotalCounts: PROCEDURE [mode: CountPrivate.Mode] RETURNS [Number] = BEGIN RETURN[Total[GetCounts[read], mode]] END; TotalTimes: PROCEDURE [mode: CountPrivate.Mode] RETURNS [Number] = BEGIN RETURN[Total[GetTimes[read], mode]] END; Total: PROCEDURE [table: TableHandle, mode: CountPrivate.Mode] RETURNS [total: Number] = BEGIN to, from, i: CARDINAL; total _ 0; IF mode = plain THEN FOR i IN [1..GetCR[read].length) DO total _ total + table.plain[i]; ENDLOOP ELSE FOR to IN [1..MaxGroup] DO FOR from IN [1..MaxGroup] DO total _ total + table.matrix[to][from]; ENDLOOP; ENDLOOP; RETURN END; Ignored: PROCEDURE [table: TableHandle, mode: CountPrivate.Mode] RETURNS [total: Number] = BEGIN to, from: CARDINAL; total _ 0; IF mode = plain THEN RETURN[table.plain[0]] ELSE { FOR to IN [0..MaxGroup] DO total _ total + table.matrix[to][0]; ENDLOOP; FOR from IN [1..MaxGroup] DO total _ total + table.matrix[0][from]; ENDLOOP}; RETURN END; GetCount: PROCEDURE [gfi: CARDINAL] RETURNS [Number] = BEGIN RETURN[GetValue[gfi, GetCounts[read]]] END; GetTime: PROCEDURE [gfi: CARDINAL] RETURNS [Number] = BEGIN RETURN[GetValue[gfi, GetTimes[read]]] END; GetValue: PROCEDURE [gfi: CARDINAL, table: TableHandle] RETURNS [count: Number] = BEGIN count _ 0; DO count _ count + table.plain[gfi]; gfi _ gfi + 1; IF MachineDefs.GfiToOffset[gfi] = 0 OR GetCR[read].length = gfi THEN EXIT; ENDLOOP; RETURN END; SortTable: PROCEDURE [table: TableHandle, sorting: Sorting] = BEGIN SiftUp: PROCEDURE [low, high: CARDINAL] = BEGIN t: CARDINAL; k, son: CARDINAL; k _ low; DO IF k*2 > high THEN EXIT; IF k*2 + 1 > high OR table.plain[sorting[k*2 + 1 - 1]] > table.plain[ sorting[k*2 - 1]] THEN son _ k*2 ELSE son _ k*2 + 1; IF table.plain[sorting[son - 1]] > table.plain[sorting[k - 1]] THEN EXIT; t _ sorting[son - 1]; sorting[son - 1] _ sorting[k - 1]; sorting[k - 1] _ t; k _ son; ENDLOOP; RETURN END; t, i: CARDINAL; length: CARDINAL = GetCR[read].length; NormalizeTable[table]; FOR i DECREASING IN [1..length/2] DO SiftUp[i, length] ENDLOOP; FOR i DECREASING IN [1..length) DO t _ sorting[1 - 1]; sorting[1 - 1] _ sorting[i + 1 - 1]; sorting[i + 1 - 1] _ t; SiftUp[1, i]; ENDLOOP; RETURN END; NormalizeTable: PROCEDURE [table: TableHandle] = BEGIN count: LONG CARDINAL _ 0; gfi: CARDINAL; length: CARDINAL = GetCR[read].length; FOR gfi DECREASING IN [0..length) DO count _ count + table.plain[gfi]; IF MachineDefs.GfiToOffset[gfi] = 0 THEN BEGIN table.plain[gfi] _ count; count _ 0; END ELSE table.plain[gfi] _ 0; ENDLOOP; RETURN END; GetSorting: PROCEDURE RETURNS [sorting: Sorting] = BEGIN i: CARDINAL; length: CARDINAL = GetCR[read].length; sorting _ Storage.Words[length*SIZE[LONG CARDINAL]]; FOR i IN [0..length) DO sorting[i] _ i; ENDLOOP; RETURN END; ReleaseSorting: PROCEDURE [sorting: Sorting] = BEGIN Storage.FreeWords[sorting]; RETURN END; Sorting: TYPE = POINTER TO ARRAY [0..0) OF CARDINAL; PrintString: PROCEDURE [s: STRING, columns: CARDINAL] = BEGIN Put.Text[logSW, s]; IF columns > s.length THEN Put.Blanks[logSW, columns - s.length]; RETURN END; PrintMatrix: PROCEDURE = BEGIN g: Format.NumberFormat = [base: 10, unsigned: TRUE, zerofill: FALSE, columns: 3]; to, from: GroupIndex; count, time, totalCounts, totalTime: Number; [totalCounts, totalTime] _ PrintTotals[matrix]; Put.Text[ logSW, " From -> To #Xfers %Xfers Time %Time ---- --- ----------- ------- ----------- ------"L]; Put.CR[logSW]; FOR from IN [1..MaxGroup] DO FOR to IN [1..MaxGroup] DO count _ counts.matrix[to][from]; time _ times.matrix[to][from]; IF count = 0 AND time = 0 THEN LOOP; Put.Char[logSW, Ascii.SP]; Put.Number[logSW, from, g]; Put.Text[logSW, " ->"L]; Put.Number[logSW, to, g]; WriteLongNumber[count, 16]; WritePercent[count, totalCounts, 9]; WriteConvertedTicksToMs[time, GetCR[read].pulseConversion, 15]; WritePercent[time, totalTime, 8]; Put.CR[logSW]; IF UserInput.userAbort THEN SIGNAL Aborted; ENDLOOP; ENDLOOP; Put.CR[logSW]; PrintTrailer[matrix]; Put.CR[logSW]; RETURN END; LoadMatrix: PROCEDURE = BEGIN groups: GroupHandle _ GetGroups[write]; gfi: CARDINAL; group: GroupIndex; stream: Streams.Handle _ GetFile[]; PerfCommonOps.Zero[groups, GetCR[read].length]; IF stream = NIL THEN RETURN; prevGroup _ 0; DO ENABLE Streams.End[] => EXIT; [gfi: gfi, group: group] _ GetNextPair[ stream ! BadFile => {PostError[badFile]; EXIT}]; DO groups[gfi] _ group; gfi _ gfi + 1; IF gfi >= cr.length OR MachineDefs.GfiToOffset[gfi] = 0 THEN EXIT; ENDLOOP; ENDLOOP; Streams.Destroy[stream]; Put.Line[logSW, "Matrix loaded"L]; RETURN END; GetFile: PROCEDURE RETURNS [stream: Streams.Handle] = BEGIN name: STRING _ Selection.Convert[string]; stream _ NIL; IF name # NIL THEN stream _ Streams.NewStream[name ! ANY => CONTINUE]; IF stream = NIL THEN { Storage.FreeString[name]; PostError[noFile]; RETURN[NIL]}; Storage.FreeString[name]; RETURN END; BadFile: SIGNAL = CODE; prevGroup: GroupIndex; GetNextPair: PROCEDURE [stream: Streams.Handle] RETURNS [gfi: CARDINAL, group: GroupIndex] = BEGIN OPEN String; c: CHARACTER; s: STRING _ [60]; DO c _ GetToken[stream, s]; IF c = ': AND s.length = 3 AND s[0] = 'g AND s[1] = 'f AND s[2] = 'i THEN { c _ GetToken[stream, s]; gfi _ StringToNumber[s, 10 ! InvalidNumber => GOTO badNumber]; SELECT c FROM Ascii.CR => {group _ prevGroup; RETURN}; Ascii.SP => { c _ GetToken[stream, s]; prevGroup _ group _ StringToNumber[ s, 10 ! InvalidNumber => GOTO badNumber]; RETURN}; ENDCASE => SIGNAL BadFile}; ENDLOOP; EXITS badNumber => SIGNAL BadFile; END; GetToken: PROC [stream: Streams.Handle, s: STRING] RETURNS [c: CHARACTER] = { s.length _ 0; DO SELECT c _ Streams.GetByte[stream] FROM Ascii.SP, Ascii.CR => IF s.length # 0 THEN RETURN ELSE LOOP; ',, ': => RETURN; ENDCASE => String.AppendChar[s, c]; ENDLOOP}; ShowGroups: PROCEDURE = BEGIN group: CARDINAL _ 177777B; gfi: CARDINAL; group _ Selection.Number[10 ! String.InvalidNumber => CONTINUE]; IF group NOT IN GroupIndex THEN GOTO noGood; Put.Text[logSW, "Modules for group "L]; Put.Decimal[logSW, group]; Put.Char[logSW, ':]; Put.CR[logSW]; FOR gfi IN [1..GetCR[read].length) DO IF group = GetGroups[read][gfi] THEN BEGIN name: STRING _ [40]; frame: MachineDefs.GFHandle _ MachineDefs.GfiToFrame[gfi]; IF frame = MachineDefs.NullGF THEN LOOP; CheckFrame[frame ! Aborted => GOTO noGood]; WriteFrameName[frame]; Put.Text[logSW, name]; Put.Char[logSW, Ascii.SP]; END; IF UserInput.userAbort THEN SIGNAL Aborted; ENDLOOP; Put.CR[logSW]; Put.CR[logSW]; EXITS noGood => PostError[badGroup]; END; Error: TYPE = { notOn, goofUp, version, noContext, badModule, badProcess, noFile, badFile, badMode, badGroup}; PostError: PROCEDURE [error: Error] = BEGIN MsgSW.Post[ sw: msgSW, string: SELECT error FROM notOn => "!Please Start CountTool and then try again"L, goofUp => "!Goofed up CountTool by use of Worry on/off during measurements"L, version => "!CountTool has Incorrect Version"L, noContext => "!No Context"L, badModule => "!No Module specified"L, badProcess => "!Invalid Process specified"L, noFile => "!Can't find input file"L, badFile => "!Bad matrix input file"L, badMode => "!Can't do in matrix mode"L, badGroup => "!Bad group specified"L, ENDCASE => "?"L]; END; PutHerald: PROCEDURE = BEGIN OPEN Time; s: STRING _ [22]; name: STRING _ ToolWindow.GetName[window]; Put.Line[logSW, name]; Append[s, Unpack[Current[]]]; s.length _ s.length - 3; Put.Line[logSW, s]; Put.CR[logSW]; Storage.FreeString[name]; RETURN END; SetDefaults: PROCEDURE [index: CARDINAL] = BEGIN pcr: PCR _ GetCR[write]; MakeSureOff[index]; IF index # setProcess THEN { IF process # NIL THEN process.length _ 0; [] _ FormSW.DisplayItem[cmdSW, processPos]}; IF index # modePos THEN { mode _ plain; [] _ FormSW.DisplayItem[cmdSW, modePos]}; pcr.newSession _ FALSE; PutHerald[]; RETURN END; MakeSureOff: PROCEDURE [index: CARDINAL] = { IF index # mon THEN {monitorOn _ FALSE; [] _ FormSW.DisplayItem[cmdSW, mon]}; RETURN}; EnumerateNotify: FormSW.EnumeratedNotifyProcType = { ParamNotify[sw: sw, item: item, index: index]}; ParamNotify: FormSW.NotifyProcType = BEGIN MsgSW.Clear[msgSW]; IF ~TablesExist[] THEN GOTO noTables; IF ~TablesCorrectVersion[] THEN GOTO badVersion; IF GetCR[read].newSession THEN SetDefaults[index]; SELECT index FROM mon => IF monitorOn THEN TurnOn[] ELSE IF ~TurnOff[] THEN PostError[goofUp]; zero => ZeroCounts[]; condition => ConditionBreaks[]; printtables => IF mode = plain THEN PrintTables[ ! Aborted => {Put.Line[logSW, "... aborted!"L]; CONTINUE}] ELSE PrintMatrix[ ! Aborted => {Put.Line[logSW, "... aborted!"L]; CONTINUE}]; printsorted => DisplaySorted[ ! Aborted => {Put.Line[logSW, "... aborted!"L]; CONTINUE}]; printmodule => DisplayModule[ ! Lookup.Fail => BEGIN CantFind[s]; CONTINUE END; Aborted => {Put.Line[logSW, "... aborted!"L]; CONTINUE}]; setProcess => SetProcess[]; modePos => GetCR[write].mode _ mode; loadmatrix => LoadMatrix[]; CountDefs.groups => ShowGroups[ ! Aborted => {Put.Line[logSW, "... aborted!"L]; CONTINUE}]; ENDCASE; EXITS noTables => {PostError[notOn]; MakeSureOff[index]}; badVersion => {PostError[version]; MakeSureOff[index]} END; CantFind: PROCEDURE [name: STRING] = BEGIN MsgSW.Post[sw: msgSW, string: "!"L, endOfMsg: FALSE]; MsgSW.Post[sw: msgSW, string: name]; RETURN END; Cleanup: Event.Notifier = BEGIN PutTables[why = resumeDebuggee]; SELECT why FROM newSession, abortSession => BEGIN monitorOn _ counterOn _ FALSE; FormSW.DisplayItem[cmdSW, mon]; END; ENDCASE; RETURN END; -- Mainline code cleanupItem: Event.Item _ [eventMask: Event.Masks[abortSession] + Event.Masks[newSession] + Event.Masks[ resumeDebuggee], eventProc: Cleanup]; Event.AddNotifier[@cleanupItem]; END.