Celtics.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) June 24, 1985 8:48:34 pm PDT
DIRECTORY
AMModel USING [ParentSection, Section, SectionClass, SectionName, SectionSource, Source, SourceObj],
AMModelLocation USING [CodeLocation, EntryLocations],
AMViewerOps USING [ReportProc, SectionFromSelection, ViewerFromSection],
BackStop USING [Call],
BasicTime USING [GetClockPulses, Pulses, PulsesToMicroseconds],
Commander USING [CommandProc, Register],
FastBreak USING [ClearFastBreak, FastBreakId, SetFastBreak],
IO USING [PutFR],
Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc],
MessageWindow USING [Append],
NumberLabels USING [CreateNumber, NumberLabel, NumberLabelUpdate],
PrincOps USING [BytePC, FrameCodeBase],
Process USING [Detach, Milliseconds, MsecToTicks, SetTimeout],
Rope USING [Cat, ROPE],
ViewerOps USING [ChangeColumn, ComputeColumn, PaintViewer, SetMenu, SetOpenHeight],
VTables USING [Create, ExchangeRows, GetTableEntry, Install, NullBorder, SetRowsAndColumns, SetTableEntry, VTable];
Celtics: CEDAR MONITOR
IMPORTS AMModel, AMModelLocation, AMViewerOps, BackStop, BasicTime, Commander, FastBreak, IO, Menus, MessageWindow, NumberLabels, Process, Rope, ViewerOps, VTables
= BEGIN
T Y P E S
BytePC: TYPE = PrincOps.BytePC;
CARD: TYPE = LONG CARDINAL;
CodeLocation: TYPE = AMModelLocation.CodeLocation;
CodeLocationList: TYPE = LIST OF CodeLocation;
FrameCodeBase: TYPE = PrincOps.FrameCodeBase;
ROPE: TYPE = Rope.ROPE;
Section: TYPE = AMModel.Section;
Source: TYPE = AMModel.Source;
SourceObj: TYPE = AMModel.SourceObj;
BaseData: TYPE = REF BaseDataRep;
BaseDataRep: TYPE = RECORD [
vtab: VTables.VTable ← NIL,
head: EachData ← NIL,
changeCond: CONDITION,
timeCond: CONDITION,
locked: BOOL ← FALSE,
rows: NAT ← 0,
cols: NAT ← limitCol];
EachData: TYPE = REF EachDataRep;
EachDataRep: TYPE = RECORD [
next: EachData ← NIL,
isTotal: BOOLFALSE,
lastCount: INT ← 0, -- last count displayed
id: FastBreak.FastBreakId ← NIL];
setCol: NAT = 0;
clearCol: NAT = 1;
countCol: NAT = 2;
codeCol: NAT = 3;
pcCol: NAT = 4;
posCol: NAT = 5;
nameCol: NAT = 6;
limitCol: NAT = 7;
UpdateDisplayProcess: PROC = TRUSTED {
vtab: VTables.VTable ← VTables.Create[rows: 1, columns: limitCol, name: "Celtics", x: 0, y: 2];
data: BaseData ← NEW[BaseDataRep];
pulses: BasicTime.Pulses ← BasicTime.GetClockPulses[];
menu: Menus.Menu ← Menus.CreateMenu[];
inner: PROC [data: BaseData] = TRUSTED {
newPulses: BasicTime.Pulses ← BasicTime.GetClockPulses[];
inverseSecs: REAL;
deltaMicros: CARD
BasicTime.PulsesToMicroseconds[newPulses] - BasicTime.PulsesToMicroseconds[pulses];
each: EachData ← data.head;
lag: EachData ← NIL;
index: NAT ← 1;
changed: BOOLFALSE;
newRows: NAT ← data.rows;
subTotal, grandTotal: INT ← 0;
IF deltaMicros < 100 THEN RETURN;
inverseSecs ← 1E6 / deltaMicros;
pulses ← newPulses;
WHILE NOT vtab.destroyed AND each # NIL DO
id: FastBreak.FastBreakId ← each.id;
next: EachData ← each.next;
SELECT TRUE FROM
each.isTotal => {
update the count
nl: NumberLabels.NumberLabel ← VTables.GetTableEntry[vtab, index, countCol];
current: INT ← subTotal;
last: INT ← each.lastCount;
index ← index + 1;
lag ← each;
IF nl # NIL AND current # last THEN {
Update event count
each.lastCount ← current;
NumberLabels.NumberLabelUpdate[nl, each.lastCount];
};
subTotal ← 0;
};
id = NIL => {
remove the row from the table
changed ← TRUE;
FOR i: NAT IN [index+1..data.rows) DO
VTables.ExchangeRows[vtab, i, i-1];
ENDLOOP;
IF lag = NIL THEN data.head ← next ELSE lag.next ← next;
};
ENDCASE => {
update the count
nl: NumberLabels.NumberLabel ← VTables.GetTableEntry[vtab, index, countCol];
current: INT ← id^;
last: INT ← each.lastCount;
index ← index + 1;
lag ← each;
subTotal ← subTotal + current;
grandTotal ← grandTotal + current;
IF nl # NIL AND current # last THEN {
Update event count
each.lastCount ← current;
NumberLabels.NumberLabelUpdate[nl, each.lastCount];
};
};
each ← next;
ENDLOOP;
IF NOT vtab.parent.iconic THEN
NumberLabels.NumberLabelUpdate[VTables.GetTableEntry[vtab, 0, countCol], grandTotal];
IF changed THEN InstallNewRows[data, index];
};
data.vtab ← vtab;
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "Set", proc: SetFastBreak, clientData: data]];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "Clear *", proc: ClearAll, clientData: data]];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "Reset *", proc: ResetAllCounts, clientData: data]];
Menus.AppendMenuEntry[menu, Menus.CreateEntry[
name: "AddSubTotal", proc: AddSubTotal, clientData: data]];
VTables.SetTableEntry[
table: vtab, row: 0, column: countCol, flavor: $Viewer,
clientData: NumberLabels.CreateNumber[info: [parent: vtab], chars: 9, paint: FALSE],
useMaxSize: TRUE];
ViewerOps.ChangeColumn[vtab.parent, left];
ViewerOps.SetMenu[vtab.parent, menu, FALSE];
InstallNewRows[data, 1];
SetPause[data];
UNTIL WaitForChange[data] DO
ENABLE ABORTED => EXIT;
DoUnderLock[data, inner];
ENDLOOP;
DoUnderLock[data, innerClearAll];
InstallNewRows[data, 0];
};
AddSubTotal: Menus.MenuProc = TRUSTED {
PROC [parent: REF ANY, clientData: REF ANYNIL,
mouseButton: MouseButton ← red, shift, control: BOOLFALSE]
DoUnderLock[clientData, innerAddSubTotal];
};
innerAddSubTotal: PROC [data: BaseData] = {
vtab: VTables.VTable = data.vtab;
eachData: EachData ← NEW[EachDataRep];
row: NAT = data.rows;
eachData.isTotal ← TRUE;
IF data.head = NIL
THEN data.head ← eachData
ELSE
FOR each: EachData ← data.head, each.next DO
IF each.next = NIL THEN {each.next ← eachData; EXIT};
ENDLOOP;
InstallNewRows[data, row + 1];
VTables.SetTableEntry[
table: vtab, row: row, column: clearCol,
name: "clear", proc: ClearBreak, clientData: eachData,
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: countCol, flavor: $Viewer,
clientData: NumberLabels.CreateNumber[info: [parent: vtab], chars: 9, paint: FALSE],
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: nameCol, flavor: $Text,
name: "subTotal", proc: NIL, clientData: eachData,
useMaxSize: TRUE];
VTables.Install[vtab];
};
ClarkKent: AMViewerOps.ReportProc = {
[msg: ROPE, severity: Severity]
MessageWindow.Append[msg, TRUE];
};
SetFastBreak: Menus.MenuProc = {
PROC [parent: REF ANY, clientData: REF ANYNIL,
mouseButton: MouseButton ← red, shift, control: BOOLFALSE]
DoUnderLock[clientData, innerSetFastBreak];
};
innerSetFastBreak: PROC [data: BaseData] = {
stage: ROPE ← "Can't do it, ";
inner: PROC = TRUSTED {
section: Section ← NIL;
source: Source ← NIL;
locationList: CodeLocationList ← NIL;
vtab: VTables.VTable = data.vtab;
row: NAT ← data.rows;
pos: INT;
parent: Section;
parentName: ROPE;
section ← AMViewerOps.SectionFromSelection[].section;
parent ← section;
IF AMModel.SectionClass[section] = statement THEN
parent ← AMModel.ParentSection[section];
parentName ← AMModel.SectionName[parent];
IF AMModel.SectionClass[parent] = proc THEN {
try to find the module name as well
DO
parent ← AMModel.ParentSection[parent];
IF AMModel.SectionClass[parent] # proc THEN EXIT;
ENDLOOP;
parentName ← Rope.Cat[AMModel.SectionName[parent], ".", parentName];
};
source ← AMModel.SectionSource[section];
WITH source SELECT FROM
entire: REF SourceObj[entire] => pos ← -1;
field: REF SourceObj[field] => {
pos ← field.firstCharIndex;
[] ← AMViewerOps.ViewerFromSection[section, ClarkKent];
};
ENDCASE;
locationList ← AMModelLocation.EntryLocations[section].list;
IF locationList = NIL THEN {
MessageWindow.Append["No such locations found!", TRUE];
RETURN;
};
FOR locList: CodeLocationList ← locationList, locList.rest UNTIL locList = NIL DO
loc: CodeLocation = locList.first;
code: FrameCodeBase ← loc.codeBase;
pc: BytePC = loc.pc;
eachData: EachData;
code.out ← FALSE; -- Because nobody expects the Spanish Inquisition!
eachData ← NEW[EachDataRep ← [id: FastBreak.SetFastBreak[code.longbase, pc]]];
IF data.head = NIL
THEN data.head ← eachData
ELSE
FOR each: EachData ← data.head, each.next DO
IF each.next = NIL THEN {each.next ← eachData; EXIT};
ENDLOOP;
MessageWindow.Append["Counting break set in ", TRUE];
MessageWindow.Append[parentName];
InstallNewRows[data, row + 1];
VTables.SetTableEntry[
table: vtab, row: row, column: setCol,
name: "reset", proc: ResetCount, clientData: eachData,
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: clearCol,
name: "clear", proc: ClearBreak, clientData: eachData,
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: countCol, flavor: $Viewer,
clientData: NumberLabels.CreateNumber[info: [parent: vtab], chars: 9, paint: FALSE],
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: codeCol, flavor: $Text,
name: IO.PutFR["%bB", [cardinal[LOOPHOLE[code]]]],
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: pcCol, flavor: $Text,
name: IO.PutFR["%bB", [cardinal[pc]]],
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: posCol, flavor: $Text,
name: IO.PutFR["%d", [integer[pos]]],
useMaxSize: TRUE];
VTables.SetTableEntry[
table: vtab, row: row, column: nameCol, flavor: $Text,
name: parentName,
useMaxSize: TRUE];
row ← row + 1;
ENDLOOP;
VTables.Install[vtab];
};
msg: ROPE ← BackStop.Call[inner];
IF msg # NIL THEN {
MessageWindow.Append[stage, TRUE];
MessageWindow.Append[msg];
MessageWindow.Append["."];
};
};
ClearAll: Menus.MenuProc = {
PROC [parent: REF ANY, clientData: REF ANYNIL,
mouseButton: MouseButton ← red, shift, control: BOOLFALSE]
DoUnderLock[clientData, innerClearAll];
};
innerClearAll: PROC [data: BaseData] = {
vtab: VTables.VTable = data.vtab;
FOR each: EachData ← data.head, each.next WHILE each # NIL DO
IF each.id # NIL THEN TRUSTED {
[] ← FastBreak.ClearFastBreak[each.id];
each.id ← NIL;
};
each.isTotal ← FALSE;
ENDLOOP;
};
ClearBreak: Menus.MenuProc = {
PROC [parent: REF ANY, clientData: REF ANYNIL,
mouseButton: MouseButton ← red, shift, control: BOOLFALSE]
WITH clientData SELECT FROM
each: EachData => TRUSTED {
id: FastBreak.FastBreakId = each.id;
IF id # NIL THEN {each.id ← NIL; [] ← FastBreak.ClearFastBreak[id]};
each.isTotal ← FALSE;
};
ENDCASE;
};
ResetCount: Menus.MenuProc = TRUSTED {
PROC [parent: REF ANY, clientData: REF ANYNIL,
mouseButton: MouseButton ← red, shift, control: BOOLFALSE]
WITH clientData SELECT FROM
each: EachData => {
id: FastBreak.FastBreakId = each.id;
IF id # NIL THEN id^ ← 0;
};
ENDCASE;
};
ResetAllCounts: Menus.MenuProc = {
PROC [parent: REF ANY, clientData: REF ANYNIL,
mouseButton: MouseButton ← red, shift, control: BOOLFALSE]
DoUnderLock[clientData, innerResetAllCounts];
};
innerResetAllCounts: PROC [data: BaseData] = TRUSTED {
vtab: VTables.VTable = data.vtab;
FOR each: EachData ← data.head, each.next WHILE each # NIL DO
IF each.id # NIL THEN each.id^ ← 0;
ENDLOOP;
};
fudge: NAT ← 20;
InstallNewRows: PROC [data: BaseData, rows: NAT] = TRUSTED {
IF data # NIL THEN {
vtab: VTables.VTable ← data.vtab;
IF vtab # NIL AND NOT vtab.destroyed AND rows # data.rows THEN {
parent: VTables.VTable ← vtab.parent;
smaller: BOOL ← rows < data.rows;
IF rows = 0 THEN data.cols ← 0;
VTables.SetRowsAndColumns[vtab, data.rows ← rows, data.cols];
SELECT rows FROM
0 => {};
1 => {
VTables.SetTableEntry[
table: vtab, column: codeCol, name: NIL, border: VTables.NullBorder];
VTables.SetTableEntry[
table: vtab, column: pcCol, name: NIL, border: VTables.NullBorder];
VTables.SetTableEntry[
table: vtab, column: posCol, name: NIL, border: VTables.NullBorder];
VTables.SetTableEntry[
table: vtab, column: nameCol, name: NIL, border: VTables.NullBorder];
};
2 => {
VTables.SetTableEntry[
table: vtab, column: codeCol, name: "code", border: VTables.NullBorder];
VTables.SetTableEntry[
table: vtab, column: pcCol, name: "pc", border: VTables.NullBorder];
VTables.SetTableEntry[
table: vtab, column: posCol, name: "pos", border: VTables.NullBorder];
VTables.SetTableEntry[
table: vtab, column: nameCol, name: "name", border: VTables.NullBorder];
};
ENDCASE;
VTables.Install[vtab, FALSE];
ViewerOps.SetOpenHeight[parent, vtab.wh + fudge];
IF NOT parent.iconic THEN {
ViewerOps.ComputeColumn[parent.column];
IF smaller THEN ViewerOps.PaintViewer[parent, all, TRUE, NIL];
};
}};
};
WaitForChange: ENTRY PROC [data: BaseData] RETURNS [quit: BOOL] = TRUSTED {
WaitForChange waits until there is a change to some value, then gets the lock for us.
ENABLE UNWIND => NULL;
vtab: VTables.VTable = data.vtab;
WAIT data.timeCond;
RETURN [vtab.destroyed];
};
SetPause: PROC [data: BaseData, pause: Process.Milliseconds ← 1000] = TRUSTED {
ENABLE UNWIND => NULL;
Process.SetTimeout[@data.timeCond, Process.MsecToTicks[pause]];
};
AcquireLock: ENTRY PROC [data: BaseData] = TRUSTED {
ENABLE UNWIND => NULL;
WHILE data.locked DO WAIT data.changeCond; ENDLOOP;
data.locked ← TRUE;
};
ReleaseLock: ENTRY PROC [data: BaseData] = TRUSTED {
ENABLE UNWIND => NULL;
data.locked ← FALSE;
BROADCAST data.changeCond;
};
DoUnderLock: PROC [ref: REF, inner: PROC [data: BaseData]] = TRUSTED {
WITH ref SELECT FROM
data: BaseData => {
ENABLE UNWIND => ReleaseLock[data];
AcquireLock[data];
inner[data];
ReleaseLock[data];
};
ENDCASE;
};
StartCeltics: Commander.CommandProc = TRUSTED {
Process.Detach[FORK UpdateDisplayProcess[]];
};
Commander.Register["Celtics", StartCeltics, "is a tool to take counts of fast breaks."];
END.