LizardToolDriver.mesa
Copyright © 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
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: STREAMNIL,
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: INTLAST[INT],
live: BOOLFALSE,
requestReset, requestReschedule: BOOLFALSE,
requestDump: DumpRequest ← none,
requestBiasStats: BOOLFALSE,
stopPrint: BOOLFALSE,
tracing: BOOLFALSE,
useHex: BOOLTRUE, -- 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 BOOLALL[FALSE],
pcRanges: LIST OF PCRange ← NIL,
instCountStop: LIST OF INTNIL
];
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;
ENTRY procs to manipulate the tool state atomically
SampleStats: ENTRY PROC [data: ToolData] RETURNS [ToolStats] = {
RETURN [data.newStats];
};
UpdateStats: ENTRY PROC [data: ToolData, stats: ToolStats, updateTime: BOOLTRUE] = {
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] = {
Call this proc to stop execution for good. We go around storing NIL to assist the garbage collector.
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];
};
Menu procs to respond to mouse clicks
StopMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
SetInternalControl[NARROW[clientData], stop];
};
PauseMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
SetInternalControl[NARROW[clientData], pause];
};
RunMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
td: ToolData = NARROW[clientData];
IF shift AND td.tracing THEN InvertTracing[td];
SetInternalControl[td, run];
};
StepMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
SetInternalControl[NARROW[clientData], step];
};
ResetMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
td: ToolData = NARROW[clientData];
td.requestReset ← TRUE;
};
ReschedMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
td: ToolData = NARROW[clientData];
td.requestReschedule ← TRUE;
};
StopPrintMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
td: ToolData = NARROW[clientData];
td.stopPrint ← TRUE;
};
DumpMemMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
td: ToolData = NARROW[clientData];
IF shift THEN td.requestDump ← bytes ELSE td.requestDump ← words;
};
DumpRegsMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
td: ToolData = NARROW[clientData];
td.requestDump ← regs;
};
DumpStatsMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
td: ToolData = NARROW[clientData];
td.requestDump ← stats;
};
DumpRingMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
td: ToolData = NARROW[clientData];
td.requestDump ← ring;
};
BiasStatsMenuProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
td: ToolData = NARROW[clientData];
td.requestBiasStats ← TRUE;
};
SamplePauseCondProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
ShowPauseCond[NARROW[clientData]]
};
SetPauseCondProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
td: ToolData = NARROW[clientData];
newPauseData: PauseCondTable ← NEW[PauseCondTableRep ← []];
oldPauseData: PauseCondTable = td.pauseCondTable;
st: STREAM = IO.RIS[ViewerTools.GetContents[td.pauseCondText]];
buffer: REF TEXTNEW[TEXT[64]]; -- buffer for scanning
opNameTable: HandCodingUtil.NameArray = HandCodingUtil.GetInstArray[];
startPC: LONG CARDINAL;
haveStart: BOOLFALSE;
haveDots: BOOLFALSE;
instCount: BOOLFALSE;
IF shift AND oldPauseData # NIL THEN newPauseData^ ← oldPauseData^;
if shift was held, add to old stuff
DO
tokenKind: IO.TokenKind;
token: ROPENIL;
[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 => {
This should be an instruction name. Of course we may have a pending startPC as well.
IF haveDots THEN GO TO oops;
Look up the instruction name and set the appropriate bits
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 => {
We must have a startPC to accept this token
IF Rope.Fetch[token, 0] = '# THEN instCount ← TRUE;
LOOP;
};
tokenDOUBLE => {
We must have a startPC to accept this token
IF haveDots OR NOT haveStart THEN GO TO oops;
IF NOT Rope.Equal[token, ".."] THEN GO TO oops;
haveDots ← TRUE;
LOOP;
};
tokenDECIMAL, tokenOCTAL, tokenHEX => {
This should be the start of a PC range
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;
When we get this far we have a new PC range to add
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 {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
InvertTracing[NARROW[clientData]];
};
BaseButtonProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
InvertOutputBase[NARROW[clientData]];
};
SetDumpStartButtonProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
data: ToolData = NARROW[clientData];
data.dumpStart ← NumberFromSelection[data.dumpStart];
};
SetDumpWordsButtonProc: Menus.MenuProc = TRUSTED {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
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];
};
Change logging procs
LogRegChange: LizardHeart.RegChangeProc = {
[data: REF, processor: Processor, reg: ProcessorRegister, old,new: Word]
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 = {
[data: REF, processor: Processor, addr: Word, old,new: Word]
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 = {
[data: REF, processor: Processor, addr: Word, old,new: Word]
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 = {
[data: REF, processor: Processor, thisPC: Word, inst: Inst, rest: Word]
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 {
Check for instructions that must pause
IF pauseTable.instArray[inst] THEN GO TO setPause;
Check the PC ranges
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;
Check the instruction count
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 = {
[data: REF, processor: Processor, newPC, rtnPC: Word, control: Control, cycles: INT]
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]] ];
};
};
Utility procs
GetInstAndRest: PROC [mem: SparseMemory.Base, pc: Word] RETURNS [inst: Inst, rest: Word] = {
A slow but simple routine to fetch instructions and the rest given the PC.
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]];
};
Main procs
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 {
Now we are off and running!
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]]};
Wait for the watcher to start up
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];
For now, we only support one processor
Process.SetPriority[Process.priorityBackground];
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).
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 {
Show the new status
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];
to make the memory agree with what is in the cache
WriteSparseMemory.ToStream[
data.log, mem, DragOpsCrossUtils.IntToWord[data.dumpStart],
data.dumpWords, useBytes, useBytes, testAbort];
};
regs =>
LizardToolOutput.ShowRegisters[log, processor, testAbort];
ring =>
Dump the last N instructions
LizardToolOutput.ShowRing[log, data.lastInstTable, mem, LastInstMod, testAbort];
stats =>
Dump the current statistics
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]};
Snapshot the statistics
newBias: LizardToolOutput.StatSnapshot ←
[pStats: processor.stats,
discarded: processor.instBuffer.bytesDiscarded, -- not in stats!!!
ifuStats: processor.ifuCache.stats,
euStats: processor.euCache.stats,
iStats: data.instTable^];
display the statistics
LizardToolOutput.ShowBiasedStats[data.log, data.biasStats, newBias];
save the bias
data.biasStats ← newBias;
IO.PutRope[data.log, "\nNote: Statistics biased.\n\n"];
};
The following procedures are for the convenience of the Quad command.
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.