RosemaryUserImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Barth, February 17, 1987 10:57:59 am PST
Bertrand Serlet October 17, 1986 10:07:27 pm PDT
Last Edited by: Gasbarro October 24, 1986 12:39:47 pm PDT
Jean-Marc Frailong November 3, 1986 5:36:54 pm PST
DIRECTORY Atom, Basics, Buttons, ChoiceButtons, Containers, Convert, Core, CoreClasses, CoreFlat, CoreOps, FS, HashTable, IO, List, PlotGraph, Ports, Process, ProcessProps, Rope, ReadEvalPrint, Real, Rosemary, RosemaryUser, Rules, ViewerClasses, ViewerIO, ViewerOps, ViewerTools;
RosemaryUserImpl: CEDAR MONITOR
IMPORTS Buttons, ChoiceButtons, Containers, Convert, CoreClasses, CoreFlat, CoreOps, FS, HashTable, IO, List, PlotGraph, Ports, Process, ProcessProps, Rope, ReadEvalPrint, Real, Rosemary, Rules, ViewerIO, ViewerOps, ViewerTools
EXPORTS RosemaryUser
= BEGIN OPEN RosemaryUser;
entryHeight: NAT = 15; -- how tall to make each line of items
entryVSpace: NAT = 4;  -- vertical leading space between lines
pointsPerInch: NAT = 72;  -- horizontal space for text ropes
borderOffset: NAT = 2;
Column: TYPE = NAT [0..3);
ColumnStart: ARRAY Column OF NAT = [entryVSpace, 1*pointsPerInch+entryVSpace, 4*pointsPerInch+entryVSpace];
testProcTable: HashTable.Table ← HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
DisplayViewer: PUBLIC PROC [simulation: Rosemary.Simulation, cellType: Core.CellType, name: ROPENIL, displayWires: CoreFlat.FlatWires ← NIL, graphWires: CoreFlat.FlatWires ← NIL] RETURNS [handle: RoseDisplay] = {
viewer: ViewerClasses.Viewer← Containers.Create[[  
name: name,
iconic: FALSE,
column: left,
scrollable: FALSE ]];
handle ← NEW[RoseDisplayRec];
handle.simulation ← simulation;
handle.cellType ← cellType;
MakeDisplayAndBrowse[displayWires: displayWires, graphWires: graphWires, height: entryVSpace, handle: handle, viewer: viewer, name: name];
};
TestProcedureViewer: PUBLIC PROC [cellType: Core.CellType, testButtons: LIST OF ROPE, name: ROPENIL, displayWires: CoreFlat.FlatWires ← NIL, graphWires: CoreFlat.FlatWires ← NIL, cutSet: CoreFlat.CutSet ← NIL, historySize: NAT ← 0, steady: BOOLFALSE, recordDeltas: BOOLTRUE] RETURNS [tester: Tester] = {
viewer: ViewerClasses.Viewer← Containers.Create[[  
name: name,
iconic: FALSE,
column: left,
scrollable: FALSE ]];
height: CARDINAL ← entryVSpace;
tester ← NEW[TesterRec];
tester.display ← NEW[RoseDisplayRec];
tester.steadyInit ← steady;
tester.recordDeltas ← recordDeltas;
tester.display.cutSet ← cutSet;
tester.display.cellType ← cellType;
tester.intermediatePort ← Ports.CreatePort[cellType, TRUE];
tester.display.simulation ← Rosemary.Instantiate[cellType, tester.intermediatePort, cutSet, historySize];
IF historySize>0 THEN {
tester.historySize ← historySize;
tester.testVectorBuffer ← NEW[Rosemary.PortSequenceRec[historySize]];
FOR b: NAT IN [0..historySize) DO
tester.testVectorBuffer[b] ← Ports.CreatePort[cellType, TRUE];
ENDLOOP;
};
[] ← Buttons.Create[info: [name: "Start Test",
wx: ColumnStart[0], wy: height,
ww: 0, wh: entryHeight,
parent: viewer, border: TRUE], clientData: tester, proc: ButtonStartTest];
tester.runState ← ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[1], y: height-borderOffset, title: "Run State:", textViewerWidth: 2*pointsPerInch].textViewer;
ViewerTools.InhibitUserEdits[tester.runState];
ViewerTools.SetContents[tester.runState, "Idle"];
IF historySize>0 THEN tester.historyTrigger ← ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[2], y: height-borderOffset, title: "History Trigger:", textViewerWidth: 2*pointsPerInch].textViewer;
height ← height + entryHeight + entryVSpace;
[] ← Buttons.Create[info: [name: "Interrupt", wx: ColumnStart[0], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: Interrupt];
tester.evalsSinceStart ← ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[1], y: height-borderOffset, title: "Evals Since Start:", textViewerWidth: 1*pointsPerInch].textViewer;
ViewerTools.InhibitUserEdits[tester.evalsSinceStart];
IF historySize>0 THEN [] ← Buttons.Create[info: [name: "Next State", wx: ColumnStart[2], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: NextState];
height ← height + entryHeight + entryVSpace;
[] ← Buttons.Create[info: [name: "Proceed", wx: ColumnStart[0], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: Proceed];
tester.proceedUntil ← ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[1], y: height-borderOffset, title: "Proceed Until:", textViewerWidth: 1*pointsPerInch].textViewer;
IF historySize>0 THEN [] ← Buttons.Create[info: [name: "Previous State", wx: ColumnStart[2], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: PreviousState];
height ← height + entryHeight + entryVSpace;
[] ← Buttons.Create[info: [name: "Abort", wx: ColumnStart[0], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: Abort, guarded: TRUE];
[] ← Buttons.Create[info: [name: "Single Eval", wx: ColumnStart[1]+borderOffset, wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: tester, proc: SingleEval];
height ← height + entryHeight + entryVSpace;
Containers.ChildXBound[viewer, Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]]];
height ← height + entryVSpace;
IF testButtons.rest#NIL THEN {
Column: TYPE = NAT [0..3);
ColumnStart: ARRAY Column OF NAT = [entryVSpace, 2*pointsPerInch+entryVSpace, 4*pointsPerInch+entryVSpace];
column: Column ← 0;
first: Buttons.Button ← NIL;
FOR tbs: LIST OF ROPE ← testButtons, tbs.rest UNTIL tbs=NIL DO
this: Buttons.Button ← Buttons.Create[info: [name: tbs.first,
wx: ColumnStart[column], wy: height,
ww: 0, wh: entryHeight,
parent: viewer, border: TRUE], clientData: tester, proc: TestButtonProc];
tester.testButtonList ← CONS[this, tester.testButtonList];
IF first=NIL THEN first ← this;
IF column=LAST[Column] THEN {
column ← 0;
height ← height + entryHeight + entryVSpace;
}
ELSE column ← column+1;
ENDLOOP;
IF column>0 THEN height ← height + entryHeight + entryVSpace;
Buttons.SetDisplayStyle[first, $BlackOnGrey];
Containers.ChildXBound[viewer, Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]]];
height ← height + entryVSpace;
};
tester.currentTestProc ← testButtons.first;
MakeDisplayAndBrowse[displayWires, graphWires, height, tester.display, viewer, name];
};
MakeDisplayAndBrowse: PROC [displayWires: CoreFlat.FlatWires, graphWires: CoreFlat.FlatWires, height: CARDINAL, handle: RoseDisplay, viewer: ViewerClasses.Viewer, name: ROPE] = {
handle.wDir ← ProcessProps.GetProp[$WorkingDirectory];
{
rep: ReadEvalPrint.Handle;
[handle.tsin, handle.tsout] ← ViewerIO.CreateViewerStreams[name: Rope.Cat[name, " Rosemary Script"]];
rep ← ReadEvalPrint.CreateStreamEvaluator[clientProc: ExploreDesign, prompt: "%l> %l", in: handle.tsin, out: handle.tsout, topLevel: TRUE];
rep.clientData ← handle;
ReadEvalPrint.MainLoop[h: rep, properties: NIL];
};
IF displayWires#NIL THEN {
Column: TYPE = NAT [0..2);
ColumnStart: ARRAY Column OF NAT = [entryVSpace, 3*pointsPerInch+pointsPerInch/4+entryVSpace];
column: Column ← 0;
count: INT ← 0;
FOR dws: CoreFlat.FlatWires ← displayWires, dws.rest UNTIL dws=NIL DO
count ← count + 1;
ENDLOOP;
IF count>100 THEN IO.PutF[handle.tsout, "You really don't want to display %g wires, maybe you should try again", IO.int[count]]
ELSE FOR dws: CoreFlat.FlatWires ← displayWires, dws.rest UNTIL dws=NIL DO
dn: ROPE ← CoreFlat.WirePathRope[handle.cellType, dws.first^];
IF Rope.Equal[Rope.Substr[dn, 0, 7], "public."] THEN dn ← Rope.Substr[dn, 7];
handle.displayWires ← CONS[ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[column], y: height-borderOffset, title: dn, textViewerWidth: 2*pointsPerInch, clientdata: dws.first], handle.displayWires];
ViewerTools.InhibitUserEdits[handle.displayWires.first.textViewer];
IF column=LAST[Column] THEN {
column ← 0;
height ← height + entryHeight + entryVSpace;
}
ELSE column ← column+1;
ENDLOOP;
IF column>0 THEN height ← height + entryHeight + entryVSpace;
Containers.ChildXBound[viewer, Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]]];
height ← height + entryVSpace;
};
[] ← Buttons.Create[info: [name: "Update", wx: ColumnStart[0], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: handle, proc: UpdateDisplayButton];
[] ← Buttons.Create[info: [name: "Log", wx: ColumnStart[1], wy: height, ww: 0, wh: entryHeight, parent: viewer, border: TRUE], clientData: handle, proc: LogChanges];
height ← height + entryHeight + entryVSpace;
handle.path ← ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[0], y: height-borderOffset, title: "Instantiation Path:", textViewerWidth: 4*pointsPerInch].textViewer;
height ← height + entryHeight + entryVSpace;
handle.currentWire ← ChoiceButtons.BuildTextPrompt[viewer: viewer, x: ColumnStart[0], y: height-borderOffset, title: "Current Wire:", textViewerWidth: 4*pointsPerInch].textViewer;
height ← height + entryHeight + entryVSpace;
Containers.ChildXBound[viewer, Rules.Create[[parent: viewer, wy: height, ww: viewer.cw, wh: 2]]];
ViewerOps.SetOpenHeight[viewer, height];
ViewerOps.PaintViewer[viewer, all];
handle.name ← name;
FOR wires: CoreFlat.FlatWires ← graphWires, wires.rest UNTIL wires=NIL DO
IF AddWireToPlot[handle, wires.first]#NIL THEN ERROR;
ENDLOOP;
};
LogChanges: Buttons.ButtonProc = {
h: RoseDisplay ← NARROW[clientData];
IF (h.logChanges ← ~h.logChanges) THEN {
Buttons.SetDisplayStyle[NARROW[parent], $WhiteOnBlack];
}
ELSE Buttons.SetDisplayStyle[NARROW[parent], $BlackOnWhite];
};
RegisterTestProc: PUBLIC PROC [name: ROPE, proc: TestProc] = {
[] ← HashTable.Store[table: testProcTable, key: name, value: NEW[TestProc ← proc]];
};
TestButtonProc: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
selectedButton: ViewerClasses.Viewer ← NARROW[parent];
FOR l: LIST OF Buttons.Button ← h.testButtonList, l.rest WHILE l#NIL DO
Buttons.SetDisplayStyle[l.first, IF l.first = selectedButton THEN $BlackOnGrey ELSE $BlackOnWhite];
ENDLOOP;
h.currentTestProc ← selectedButton.name;
};
AbortSignal: SIGNAL = CODE;
LogSettle: PUBLIC PROC [handle: RoseDisplay, time: INT] = {
UpdateWire: Rosemary.UpdateProc = {
coreWire: CoreFlat.FlatWireRec;
RecordDelta[handle, roseWire, time];
IF NOT handle.logChanges THEN RETURN;
UpdateDisplay[handle];
coreWire ← roseWire.wire;
wireKey^ ← coreWire;
IO.PutF[handle.tsout, "%g←%g ", IO.rope[CoreFlat.WirePathRope[handle.cellType, coreWire]], IO.rope[Ports.LevelSequenceToRope[Rosemary.WireValue[ handle.simulation, wireKey]]]];
};
wireKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
Rosemary.Settle[handle.simulation, UpdateWire];
DeltaFinished[handle, time];
IF handle.logChanges THEN IO.PutRope[handle.tsout, "\n\n"];
};
ButtonStartTest: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
StartTest[h];
};
StartTest: PUBLIC PROC [tester: Tester] = {
GrumbleStartTest: PROC = {
IF NOT AlreadyStarted[tester] THEN DoStartTest[tester];
};
ProcessProps.AddPropList[LIST[NEW[Atom.DottedPairNode ← [key: $WorkingDirectory, val: tester.display.wDir]]], GrumbleStartTest]
};
DoStartTest: PROC [h: Tester] = {
Eval: PROC [memory: BOOLTRUE] = {
UpdateWire: Rosemary.UpdateProc = {
coreWire: CoreFlat.FlatWireRec;
IF h.abort THEN SIGNAL AbortSignal;
IF h.recordDeltas THEN RecordDelta[h.display, roseWire, h.evalSinceStartCount+1];
IF h.display.logChanges THEN {
UpdateDisplay[h.display];
coreWire ← roseWire.wire;
wireKey^ ← coreWire;
IO.PutF[h.display.tsout, "%g←%g ", IO.rope[CoreFlat.WirePathRope[h.display.cellType, coreWire]], IO.rope[Ports.LevelSequenceToRope[Rosemary.WireValue[ h.display.simulation, wireKey]]]];
};
};
DO
inBuf: BOOL ← h.displayedStatePoint#h.currentStatePoint AND h.validStates>0;
testPort: Ports.Port ← IF inBuf THEN h.testVectorBuffer[NextBuf[h, h.displayedStatePoint]] ELSE currentTestPort;
Ports.CopyPortValue[from: testPort, to: h.intermediatePort];
Rosemary.Settle[h.display.simulation, UpdateWire, memory];
IF h.display.logChanges THEN IO.PutRope[h.display.tsout, "\n\n"];
UpdateESSC[h, h.evalSinceStartCount+1];
DeltaFinished[h.display, h.evalSinceStartCount];
IF h.evalSinceStartCount>=h.historyTriggerCount AND NOT inBuf AND h.historySize>0 THEN {
h.currentStatePoint ← NextBuf[h, h.currentStatePoint];
h.displayedStatePoint ← h.currentStatePoint;
IF h.validStates < h.historySize THEN h.validStates ← h.validStates + 1;
Rosemary.StatePoint[h.display.simulation, h.currentStatePoint];
Ports.CopyPortValue[from: currentTestPort, to: h.testVectorBuffer[h.currentStatePoint]];
};
Ports.CheckPortValue[root: h.display.cellType.public, truth: testPort, question: h.intermediatePort ! Ports.CheckError => UpdateDisplay[h.display]];
IF EvalsFinished[h] THEN SIGNAL AbortSignal;
IF inBuf THEN {
h.displayedStatePoint ← NextBuf[h, h.displayedStatePoint];
IF h.displayedStatePoint=h.currentStatePoint THEN EXIT;
check new state same as recorded state;
}
ELSE EXIT;
ENDLOOP;
};
wireKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
testProc: TestProc ← NARROW[HashTable.Fetch[table: testProcTable, key: h.currentTestProc].value, REF TestProc]^;
currentTestPort: Ports.Port ← Ports.CreatePort[h.display.cellType, TRUE];
IF testProc=NIL THEN ERROR;
UpdateESSC[h, 0];
h.validStates ← 0;
SetEvalUntil[h: h, interrupt: FALSE, abort: FALSE];
ViewerTools.SetContents[h.runState, "Initializing"];
Rosemary.Initialize[simulation: h.display.simulation, steady: h.steadyInit];
InitializeDeltas[h.display];
ViewerTools.SetContents[h.runState, "Running"];
testProc[h.display.cellType, currentTestPort, Eval ! Rosemary.Stop => IF reason = $BoolWireHasX THEN {IO.PutF[h.display.tsout, "\nWire %g settled to an X and has boolean ports", IO.rope[CoreFlat.WirePathRope[h.display.cellType, NARROW[data, CoreFlat.FlatWire]^]]]; RESUME}; AbortSignal => CONTINUE; UNWIND => FinishedTest[h]];
FinishedTest[h];
};
AlreadyStarted: ENTRY PROC [h: Tester] RETURNS [started: BOOL] = {
started ← h.testStarted OR h.singleEval;
h.testStarted ← TRUE;
BROADCAST h.proceed;
};
FinishedTest: ENTRY PROC [h: Tester] = {
UpdateDisplay[h.display];
ViewerTools.SetContents[h.runState, "Idle"];
h.testStarted ← FALSE;
BROADCAST h.proceed;
};
Interrupt: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
SetEvalUntil[h: h, interrupt: TRUE, abort: FALSE];
};
Proceed: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
SetEvalUntil[h: h, interrupt: FALSE, abort: FALSE];
};
Abort: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
SetEvalUntil[h: h, interrupt: FALSE, abort: TRUE];
};
SingleEval: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
DoSingleEval[h];
SetEvalUntil[h: h, interrupt: TRUE, abort: FALSE];
};
DoSingleEval: ENTRY PROC [h: Tester] = {
h.singleEval ← TRUE;
IF h.testStarted THEN {
h.interrupt ← TRUE;
UNTIL h.waiting OR NOT h.testStarted DO WAIT h.proceed ENDLOOP;
IF h.testStarted THEN {
count: INT ← h.evalSinceStartCount;
until: INT ← h.evalUntil;
h.evalUntil ← count+1;
h.interrupt ← FALSE;
BROADCAST h.proceed;
UNTIL h.evalSinceStartCount > count OR h.abort OR NOT h.testStarted DO
WAIT h.proceed;
ENDLOOP;
h.interrupt ← TRUE;
h.evalUntil ← until;
};
}
ELSE TRUSTED {
h.evalUntil ← 1;
h.testStarted ← TRUE;
Process.Detach[FORK DoStartTest[h]];
UNTIL h.waiting OR h.abort DO WAIT h.proceed ENDLOOP;
};
h.singleEval ← FALSE;
};
NextState: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
DoNextState[h];
};
DoNextState: ENTRY PROC [h: Tester] = {
IF h.waiting AND NOT h.singleEval THEN {
IF h.displayedStatePoint=h.currentStatePoint THEN IO.PutRope[h.display.tsout, "No next state"]
ELSE {
h.displayedStatePoint ← NextBuf[h, h.displayedStatePoint];
Rosemary.RestoreState[h.display.simulation, h.displayedStatePoint];
UpdateESSC[h, h.evalSinceStartCount+1];
UpdateDisplay[h.display];
};
};
};
PreviousState: Buttons.ButtonProc = {
h: Tester ← NARROW[clientData];
DoPreviousState[h];
};
DoPreviousState: ENTRY PROC [h: Tester] = {
IF h.waiting AND NOT h.singleEval THEN {
ndsp: NATIF h.displayedStatePoint=0 THEN h.historySize-1 ELSE h.displayedStatePoint-1;
SELECT TRUE FROM
ndsp>=h.validStates => IO.PutRope[h.display.tsout, "No previous state, not enough evals since history trigger"];
ndsp=h.currentStatePoint => IO.PutRope[h.display.tsout, "No previous state, buffer depth exceeded"];
ENDCASE => {
h.displayedStatePoint ← ndsp;
Rosemary.RestoreState[h.display.simulation, h.displayedStatePoint];
UpdateESSC[h, h.evalSinceStartCount-1];
UpdateDisplay[h.display];
};
};
};
SetProceedUntil: PUBLIC PROC [tester: Tester, count: INT] = {
ViewerTools.SetContents[tester.proceedUntil, Convert.RopeFromInt[from: count, showRadix: FALSE]];
};
SetEvalUntil: PROC [h: Tester, interrupt: BOOL, abort: BOOL] = {
GetCount: PROC [v: ViewerClasses.Viewer] RETURNS [count: INT] = {
IF v=NIL THEN count ← 0
ELSE {
vRope: ROPE ← ViewerTools.GetContents[v];
count ← LAST[INT];
IF vRope#NIL THEN count ← Convert.IntFromRope[vRope ! Convert.Error => CONTINUE];
};
};
SetUntil[h, GetCount[h.proceedUntil], GetCount[h.historyTrigger], interrupt, abort];
};
SetUntil: ENTRY PROC [h: Tester, count: INT, trigger: INT, interrupt: BOOL, abort: BOOL] = {
IF NOT h.singleEval THEN {
h.evalUntil ← count;
h.historyTriggerCount ← trigger;
IF trigger>h.evalSinceStartCount THEN h.validStates ← 0;
h.interrupt ← interrupt;
};
h.abort ← abort;
BROADCAST h.proceed;
};
EvalsFinished: ENTRY PROC [h: Tester] RETURNS [abort: BOOL] = {
resetRunState: BOOLFALSE;
WHILE (h.evalUntil<=h.evalSinceStartCount OR h.interrupt) AND NOT h.abort DO
UpdateDisplay[h.display];
ViewerTools.SetContents[h.runState, "Interrupted"];
resetRunState ← TRUE;
BROADCAST h.proceed;
h.waiting ← TRUE;
WAIT h.proceed;
h.waiting ← FALSE;
ENDLOOP;
IF resetRunState THEN ViewerTools.SetContents[h.runState, "Running"];
abort ← h.abort;
};
UpdateDisplayButton: Buttons.ButtonProc = {
handle: RoseDisplay ← NARROW[clientData];
UpdateDisplay[handle];
};
UpdateDisplay: PUBLIC PROC [handle: RoseDisplay] = {
FOR dwvs: LIST OF ChoiceButtons.PromptDataRef ← handle.displayWires, dwvs.rest UNTIL dwvs=NIL DO
dw: CoreFlat.FlatWire ← NARROW[dwvs.first.clientdata];
new: ROPE;
value: Ports.LevelSequence ← Rosemary.WireValue[handle.simulation, dw];
new ← Ports.LevelSequenceToRope[value, CoreOps.WireBits[dw.wire]];
IF Rope.Length[new] > 16 THEN new ← "Typescript";
IF NOT Rope.Equal[new, ViewerTools.GetContents[dwvs.first.textViewer]] THEN ViewerTools.SetContents[dwvs.first.textViewer, new];
ENDLOOP;
};
NextBuf: PROC [h: Tester, buf: NAT] RETURNS [next: NAT] = {
next ← (buf + 1) MOD h.historySize;
};
UpdateESSC: PROC [h: Tester, new: INT] = {
h.evalSinceStartCount ← new;
ViewerTools.SetContents[h.evalsSinceStart, Convert.RopeFromCard[from: h.evalSinceStartCount, showRadix: FALSE]];
};
DebugEvalProcSignal: SIGNAL [port: Ports.Port, state: REF ANY] = CODE;
DebugEvalProc: PROC [port: Ports.Port, state: REF ANY] = {
SIGNAL DebugEvalProcSignal[port, state];
};
ExploreDesign: ReadEvalPrint.ClientProc = {
ENABLE CoreFlat.PathError => {result ← msg; GOTO Done};
Chop: PROC RETURNS [first, rest: ROPENIL ] = {
dStream: IO.STREAM = IO.RIS[command];
first ← dStream.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token;
rest ← command.Substr[dStream.GetIndex];
};
handle: RoseDisplay ← NARROW[h.clientData];
operation, rest, pathName, fullPathName, wireName: ROPE;
[operation, rest] ← Chop[];
pathName ← ViewerTools.GetContents[handle.path];
fullPathName ← Rope.Cat[pathName, rest];
wireName ← Rope.Cat[pathName, ViewerTools.GetContents[handle.currentWire], rest];
SELECT TRUE FROM
Rope.Equal[operation, "a"] => {
wireKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
wireKey^ ← CoreFlat.ParseWirePath[handle.cellType, wireName, handle.cutSet];
result ← AddWireToPlot[handle, wireKey];
};
Rope.Equal[operation, "c"] => {
resultStream: IO.STREAMIO.ROS[];
flatCell: CoreFlat.FlatCellTypeRec ← CoreFlat.ParseCellTypePath[handle.cellType, fullPathName, handle.cutSet];
cellType: Core.CellType ← CoreFlat.ResolveFlatCellType[handle.cellType, flatCell].cellType;
CoreOps.PrintCellType[cellType, resultStream];
result ← IO.RopeFromROS[resultStream];
};
Rope.Equal[operation, "cn"] => {
flatCell: CoreFlat.FlatCellTypeRec ← CoreFlat.ParseCellTypePath[handle.cellType, fullPathName, handle.cutSet];
cellType: Core.CellType ← CoreFlat.ResolveFlatCellType[handle.cellType, flatCell].cellType;
result ← CoreOps.InheritCellTypeName[cellType];
};
Rope.Equal[operation, "cp"] => {
resultStream: IO.STREAMIO.ROS[];
flatCell: CoreFlat.FlatCellTypeRec ← CoreFlat.ParseCellTypePath[handle.cellType, fullPathName, handle.cutSet];
cellType: Core.CellType ← CoreFlat.ResolveFlatCellType[handle.cellType, flatCell].cellType;
CoreOps.PrintWire[cellType.public, resultStream];
result ← Rope.Cat[CoreOps.InheritCellTypeName[cellType], ": ", IO.RopeFromROS[resultStream]];
};
Rope.Equal[operation, "d"] => result ← DescribeWire[goryDetails: TRUE, handle: handle, wireName: wireName];
Rope.Equal[operation, "p"] => {
PrintPort: PROC [public: Core.Wire] RETURNS [subWires: BOOLTRUE, quit: BOOLFALSE] = {
IF NOT Ports.WirePortType[cellType, public].levelType=composite THEN {
value: Ports.LevelSequence;
subWires ← FALSE;
thisWire.wire ← public;
result ← Rope.Cat[result, CoreOps.GetFullWireName[cellType.public, public], " - "];
value ← Rosemary.WireValue[handle.simulation, thisWire ! Rosemary.NotInstantiated => GOTO NotFound];
result ← Rope.Cat[result, Ports.LevelSequenceToRope[value], "\n"];
EXITS NotFound => result ← Rope.Cat[result, "No such wire\n"];
};
};
flatCell: CoreFlat.FlatCellTypeRec;
thisWire: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
cellType: Core.CellType;
flatCell ← CoreFlat.ParseCellTypePath[handle.cellType, fullPathName, handle.cutSet];
cellType ← CoreFlat.ResolveFlatCellType[handle.cellType, flatCell].cellType;
thisWire.flatCell ← flatCell;
thisWire.wireRoot ← public;
IF CoreOps.VisitWire[cellType.public, PrintPort] THEN ERROR;
};
Rope.Equal[operation, "r"] => {
wireKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
wireKey^ ← CoreFlat.ParseWirePath[handle.cellType, wireName, handle.cutSet];
IF NOT RemoveWireFromPlot[handle, wireKey] THEN result ← "No such wire plotted";
};
Rope.Equal[operation, "s"] => {
flatCell: CoreFlat.FlatCellType ← NEW[CoreFlat.FlatCellTypeRec];
roseInstance: Rosemary.RoseCellInstance;
flatCell^ ← CoreFlat.ParseCellTypePath[handle.cellType, fullPathName, handle.cutSet];
roseInstance ← NARROW[HashTable.Fetch[table: handle.simulation.coreToRoseInstances, key: flatCell].value];
IF roseInstance=NIL THEN result ← "No such instance"
ELSE TRUSTED {Process.Detach[FORK DebugEvalProc[roseInstance.publicPort, roseInstance.state]]};
};
Rope.Equal[operation, "t"] => result ← DescribeWire[goryDetails: FALSE, handle: handle, wireName: wireName];
Rope.Equal[operation, "v"] => result ← WireValueRope[handle, wireName];
ENDCASE => IF NOT Rope.IsEmpty[operation] THEN result ← Rope.Cat["No such command: ", operation, ", commands are (a)dd wire to plot, (r)emove wire from plot, (s)tate, (v)alue, (t)race, (p)orts, (c)elltype, (c)elltype(n)ame, (c)elltype(p)ublic, and (d)rivers"];
EXITS
Done => NULL;
};
WireValueRope: PROC [handle: RoseDisplay, wireName: ROPE] RETURNS [rope: ROPE] = {
key: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
key^ ← CoreFlat.ParseWirePath[handle.cellType, wireName, handle.cutSet];
rope ← Ports.LSToRope[Rosemary.WireValue[handle.simulation, key ! Rosemary.NotInstantiated => GOTO NotFound]];
EXITS NotFound => rope ← "No such wire";
};
DescribeWire: PROC [goryDetails: BOOL, handle: RoseDisplay, wireName: ROPE] RETURNS [result: ROPE] = {
wireKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
parentKey: CoreFlat.FlatWire ← NEW[CoreFlat.FlatWireRec];
roseWire: Rosemary.RoseWire;
wireKey^ ← CoreFlat.ParseWirePath[handle.cellType, wireName, handle.cutSet];
parentKey^ ← CoreFlat.CanonizeWire[handle.cellType, wireKey^];
roseWire ← NARROW[HashTable.Fetch[handle.simulation.coreToRoseWires, parentKey].value];
result ← IF roseWire=NIL THEN Rope.Cat[CoreFlat.WirePathRope[handle.cellType, wireKey^], " = ", WireValueRope[handle, wireName], "\nThis wire is not stored as a Rosemary wire, try a parent or child of it"]
ELSE RoseWirePrint[goryDetails, handle.simulation, handle.cellType, roseWire];
};
RoseWirePrint: PROC [goryDetails: BOOL, simulation: Rosemary.Simulation, root: Core.CellType, roseWire: Rosemary.RoseWire] RETURNS [result: ROPE] = {
CatWire: PROC [roseWire: Rosemary.RoseWire] = {
result ← Rope.Cat[result, CoreFlat.WirePathRope[root, roseWire.wire], " - "];
IF roseWire.currentValue=NIL THEN {
IF goryDetails THEN {
result ← Rope.Cat[result, "cd: ", Ports.driveNames[roseWire.connectionDrive]];
result ← Rope.Cat[result, ", sd: ", Ports.driveNames[roseWire.switchDrive]];
result ← Rope.Cat[result, ", ud: ", Ports.driveNames[roseWire.upDrive]];
result ← Rope.Cat[result, ", dd: ", Ports.driveNames[roseWire.downDrive]];
result ← Rope.Cat[result, ", wd: ", Ports.driveNames[roseWire.wireDrive]];
result ← Rope.Cat[result, ", cl: ", Ports.levelNames[roseWire.connectionLevel], ", "];
};
result ← Rope.Cat[result, "wl: ", Ports.levelNames[roseWire.wireLevel]];
}
ELSE result ← Ports.LevelSequenceToRope[roseWire.currentValue];
};
CatTran: PROC [roseTran: Rosemary.RoseTransistor] = {
result ← Rope.Cat[result, " tt: ", CoreClasses.transistorTypeNames[roseTran.type]];
IF goryDetails THEN result ← Rope.Cat[result, ", tc: ", Ports.driveNames[roseTran.conductivity]];
};
FindPortWire: Ports.EachWirePortPairProc = {
IF (quit ← port=clientPort) THEN coreWire.wire ← wire;
};
clientPort: Ports.Port;
coreWire: CoreFlat.FlatWireRec;
coreWire.wireRoot ← public;
CatWire[roseWire];
result ← Rope.Cat[result, "\nConnections\n"];
IF roseWire.connections#NIL THEN FOR fields: NAT IN [0..roseWire.connections.size) DO
field: Rosemary.Field ← roseWire.connections[fields];
roseInstance: Rosemary.RoseCellInstance ← field.portBinding.instance;
result ← Rope.Cat[result, " "];
clientPort ← field.portBinding.clientPort;
IF NOT Ports.VisitBinding[wire: IF roseInstance=NIL THEN root.public ELSE CoreFlat.ResolveFlatCellType[root, roseInstance.instance].cellType.public, port: IF roseInstance=NIL THEN simulation.testPort ELSE roseInstance.publicPort, eachWirePortPair: FindPortWire] THEN ERROR;
coreWire.flatCell ← IF roseInstance=NIL THEN CoreFlat.rootCellType ELSE roseInstance.instance;
result ← Rope.Cat[result, CoreFlat.WirePathRope[root, coreWire]];
IF goryDetails THEN {
result ← Rope.Cat[result, " - d: "];
IF clientPort.driveType=aggregate THEN result ← Rope.Cat[result, Ports.driveNames[field.portBinding.currentDrive]]
ELSE FOR i: NAT IN [0..field.currentDrive.size) DO
IF i=0 THEN result ← Rope.Cat[result, "("];
result ← Rope.Cat[result, Ports.driveNames[field.currentDrive[i]]];
result ← Rope.Cat[result, IF i=field.currentDrive.size-1 THEN ")" ELSE ", "];
ENDLOOP;
result ← Rope.Cat[result, ", v: ", Ports.LevelSequenceToRope[field.currentValue]];
result ← Rope.Cat[result, ", sb: ", Convert.RopeFromCard[field.portStartBit]];
result ← Rope.Cat[result, ", fs: ", Convert.RopeFromCard[field.currentValue.size]];
};
result ← Rope.Cat[result, "\n"];
ENDLOOP;
result ← Rope.Cat[result, "Channels\n"];
IF roseWire.channels#NIL THEN FOR channels: NAT IN [0..roseWire.channels.size) DO
tran: Rosemary.RoseTransistor ← roseWire.channels[channels];
otherWire: Rosemary.RoseWire ← tran.ch1;
CatTran[tran];
result ← Rope.Cat[result, "\n oc: "];
IF otherWire=roseWire THEN otherWire ← tran.ch2;
CatWire[otherWire];
result ← Rope.Cat[result, "\n g: ", ];
CatWire[tran.gate];
result ← Rope.Cat[result, "\n"];
ENDLOOP;
result ← Rope.Cat[result, "Gates\n"];
IF roseWire.gates#NIL THEN FOR gates: NAT IN [0..roseWire.gates.size) DO
tran: Rosemary.RoseTransistor ← roseWire.gates[gates];
CatTran[tran];
result ← Rope.Cat[result, "\n c1: "];
CatWire[tran.ch1];
result ← Rope.Cat[result, "\n c2: ", ];
CatWire[tran.ch2];
result ← Rope.Cat[result, "\n"];
ENDLOOP;
};
Wire Display
DisplayPortLeafWires: PUBLIC PROC [root: Core.CellType, flatCell: CoreFlat.FlatCellTypeRec ← CoreFlat.rootCellType] RETURNS [displayWires: CoreFlat.FlatWires] = {
CompareFlatWires: List.CompareProc = {
flat1: CoreFlat.FlatWire ← NARROW[ref1];
flat2: CoreFlat.FlatWire ← NARROW[ref2];
IF flat1.flatCell.path.length>flat2.flatCell.path.length THEN RETURN [greater];
IF flat1.flatCell.path.length<flat2.flatCell.path.length THEN RETURN [less];
FOR i: CoreFlat.InstancePathIndex IN [0..flat1.flatCell.path.length) DO
one: BOOL ← flat1.flatCell.path.bits[i];
other: BOOL ← flat2.flatCell.path.bits[i];
SELECT TRUE FROM
one AND (NOT other) => RETURN [greater];
(NOT one) AND other => RETURN [less];
ENDCASE;
ENDLOOP;
IF flat1.flatCell.recastCount>flat2.flatCell.recastCount THEN RETURN [greater];
IF flat1.flatCell.recastCount<flat2.flatCell.recastCount THEN RETURN [less];
RETURN[Rope.Compare[CoreOps.GetFullWireName[cell.public, flat1.wire], CoreOps.GetFullWireName[cell.public, flat2.wire]]];
};
FindLeaves: CoreOps.EachWireProc = {
IF Ports.WirePortType[cell, wire].levelType#composite THEN {
subWires ← FALSE;
displayWires ← CONS[NEW[CoreFlat.FlatWireRec ← [flatCell: flatCell, wireRoot: public, wire: wire]], displayWires];
};
};
cell: Core.CellType ← CoreFlat.ResolveFlatCellType[root, flatCell].cellType;
IF CoreOps.VisitWire[cell.public, FindLeaves] THEN ERROR;
TRUSTED {displayWires ← LOOPHOLE[List.Sort[LOOPHOLE[displayWires], CompareFlatWires]]};
};
Plotting
RoseGraphClass: PlotGraph.GraphClass ← NEW[PlotGraph.GraphClassRec ← [
enumerate: RoseGraphEnumerate]];
AddWireToPlot: PUBLIC PROC [handle: RoseDisplay, wire: CoreFlat.FlatWire] RETURNS [msg: Core.ROPENIL] = {
roseValues: Rosemary.RoseValues ← Rosemary.GetValues[handle.simulation, wire ! Rosemary.NotInstantiated => GOTO NoSuchWire];
graphPlotData: GraphPlotData ← NEW[GraphPlotDataRec];
plot: PlotGraph.Plot ← handle.plot;
axis: PlotGraph.Axis ← NEW[PlotGraph.AxisRec ← [
bounds: [0.0, 0.0, 10.0, 6.0],
name: CoreFlat.WirePathRope[handle.cellType, wire^],
axisData: [[
ticks: 1.0,
visible: TRUE,
grid: FALSE],[
ticks: 1.0,
visible: TRUE,
grid: FALSE]]]];
graph: PlotGraph.Graph ← NEW[PlotGraph.GraphRec ← [
class: RoseGraphClass,
data: graphPlotData]];
size: NAT ← 0;
graphPlotData.roseValues ← roseValues;
[graphPlotData.bits, graphPlotData.parts, size] ← BitsAndParts[handle, roseValues];
IF Rope.Equal[Rope.Substr[axis.name, 0, 7], "public."] THEN axis.name ← Rope.Substr[axis.name, 7];
IF roseValues.rest=NIL AND roseValues.first.fieldWidth=1 THEN axis.style ← analog
ELSE {
axis.style ← hexaV;
axis.maxChars ← (size+3)/4;
};
IF plot=NIL THEN {
plot ← handle.plot ← PlotGraph.CreatePlot[handle.name];
PlotGraph.LockPlot[plot];
plot.lowerBounds ← [0.0, 0.0];
plot.upperBounds ← [Real.Float[handle.lastValidTime], 5.0];
plot.data ← handle;
}
ELSE PlotGraph.LockPlot[plot];
IF plot.axis#NIL THEN axis.bounds ← plot.axis.first.bounds;
plot.axis ← CONS[axis, plot.axis];
axis.graphs ← CONS[graph, axis.graphs];
PlotGraph.UnlockPlot[plot];
PlotGraph.RefreshPlot[plot: plot, eraseFirst: TRUE];
EXITS
NoSuchWire => msg ← "Not stored as a Rosemary wire";
};
RemoveWireFromPlot: PUBLIC PROC [handle: RoseDisplay, wire: CoreFlat.FlatWire] RETURNS [found: BOOLFALSE] = {
roseValues: Rosemary.RoseValues ← Rosemary.GetValues[handle.simulation, wire ! Rosemary.NotInstantiated => GOTO NoSuchWire];
plot: PlotGraph.Plot ← handle.plot;
trail: PlotGraph.AxisList ← NIL;
IF plot=NIL THEN RETURN;
PlotGraph.LockPlot[plot];
FOR axis: PlotGraph.AxisList ← plot.axis, axis.rest UNTIL axis=NIL DO
data: GraphPlotData ← NARROW[axis.first.graphs.first.data];
IF data.roseValues=roseValues THEN {
IF trail=NIL THEN plot.axis ← axis.rest ELSE trail.rest ← axis.rest;
found ← TRUE;
EXIT;
};
trail ← axis;
REPEAT FINISHED => found ← FALSE;
ENDLOOP;
PlotGraph.UnlockPlot[plot];
PlotGraph.RefreshPlot[plot: plot, eraseFirst: TRUE];
EXITS
NoSuchWire => NULL;
};
InitializeDeltas: PUBLIC PROC [handle: RoseDisplay] = {
SetDelta: HashTable.EachPairAction = {
roseWire: Rosemary.RoseWire ← NARROW[value];
head: GraphData ← NARROW[roseWire.data];
IF head#NIL THEN head.first ← head.last ← head.nextToLast ← head.secondToLast ← NIL;
RecordDelta[handle, roseWire, 0];
};
IF handle.ps=NIL THEN handle.ps ← FS.StreamOpen["///Temp/RoseBackingStore.bin", $create] ELSE IO.Reset[handle.ps];
IF handle.psHash=NIL THEN handle.psHash ← HashTable.Create[];
[] ← HashTable.Pairs[table: handle.simulation.coreToRoseWires, action: SetDelta];
handle.lastValidTime ← 0;
IF handle.plot#NIL THEN PlotGraph.RefreshPlot[plot: handle.plot, eraseFirst: TRUE];
};
DeltaFinished: PUBLIC PROC [handle: RoseDisplay, time: INT] = {
plot: PlotGraph.Plot ← handle.plot;
handle.lastValidTime ← time;
IF plot#NIL THEN {
oldUpper: REAL ← plot.upperBounds.x;
newUpper: REAL ← Real.Float[time];
plot.upperBounds.x ← newUpper;
PlotGraph.RefreshPlot[plot: plot, within: [oldUpper, 0.0, newUpper-oldUpper, 6.0]];
};
};
GraphPlotData: TYPE = REF GraphPlotDataRec;
GraphPlotDataRec: TYPE = RECORD [
roseValues: Rosemary.RoseValues ← NIL,
parts: GraphValueSequence ← NIL,
bits: Ports.LevelSequence ← NIL];
GraphData: TYPE = REF GraphDataRec;
GraphDataRec: TYPE = RECORD [
first, last, nextToLast, secondToLast: GraphValue ← NIL,
interested: BOOLFALSE];
GraphValue: TYPE = REF GraphValueRec;
GraphValueRec: TYPE = RECORD [
next: GraphValue ← NIL,
time: INTLAST[INT],
value: SEQUENCE size: CARDINAL OF Ports.Level];
GraphValueSequence: TYPE = REF GraphValueSequenceRec;
GraphValueSequenceRec: TYPE = RECORD [
parts: SEQUENCE size: CARDINAL OF GraphValue];
RecordDelta: PUBLIC PROC [handle: RoseDisplay, wire: Rosemary.RoseWire, time: INT] = {
head: GraphData ← NARROW[wire.data];
single: BOOL ← wire.currentValue=NIL;
size: NATIF single THEN 1 ELSE wire.currentValue.size;
new: BOOLFALSE;
LockBackingStore[handle];
IF head=NIL THEN wire.data ← head ← NEW[GraphDataRec];
IF head.last=NIL THEN new ← TRUE
ELSE IF time>head.last.time THEN {
IF single THEN new ← wire.wireLevel#head.last[0]
ELSE FOR i: NAT IN [0..size) DO
IF head.last.value[i] # wire.currentValue[i] THEN {
new ← TRUE;
EXIT;
};
ENDLOOP;
}
ELSE {
collapse: BOOLFALSE;
IF head.nextToLast#NIL THEN {
IF single THEN collapse ← head.nextToLast.value[0] = wire.wireLevel
ELSE {
collapse ← TRUE;
FOR i: NAT IN [0..size) DO
IF head.nextToLast.value[i] # wire.currentValue[i] THEN {
collapse ← FALSE;
EXIT;
};
ENDLOOP;
};
};
IF collapse THEN {
head.last.time ← LAST[INT];
head.last ← head.nextToLast;
head.nextToLast ← head.secondToLast;
head.secondToLast ← NIL;
}
ELSE IF single THEN head.last.value[0] ← wire.wireLevel
ELSE FOR i: NAT IN [0..size) DO
head.last.value[i] ← wire.currentValue[i];
ENDLOOP;
};
IF new THEN {
value: GraphValue;
IF head.secondToLast=NIL OR head.interested THEN {
value ← IF head.last=NIL OR head.last.next=NIL THEN NEW[GraphValueRec[size]] ELSE head.last.next;
IF head.first=NIL AND head.last=NIL THEN head.first ← value;
}
ELSE {
WriteLong: PROC [l: bytes Basics.LongNumber] = {
IO.PutChar[handle.ps, LOOPHOLE[l.lh]];
IO.PutChar[handle.ps, LOOPHOLE[l.ll]];
IO.PutChar[handle.ps, LOOPHOLE[l.hh]];
IO.PutChar[handle.ps, LOOPHOLE[l.hl]];
};
value ← head.secondToLast;
WriteLong[LOOPHOLE[head]];
[] ← HashTable.Insert[handle.psHash, head, head];
WriteLong[LOOPHOLE[value.time]];
FOR i: CARDINAL IN [0..value.size) DO
IO.PutChar[handle.ps, LOOPHOLE[value.value[i]]];
ENDLOOP;
value.next ← NIL;
head.first ← NIL;
};
value.time ← time;
IF single THEN value.value[0] ← wire.wireLevel
ELSE FOR i: NAT IN [0..size) DO
value.value[i] ← wire.currentValue[i];
ENDLOOP;
IF head.last#NIL THEN head.last.next ← value;
head.secondToLast ← head.nextToLast;
head.nextToLast ← head.last;
head.last ← value;
};
UnlockBackingStore[handle];
};
LockBackingStore: ENTRY PROC [h: RoseDisplay] = {
UNTIL NOT h.psLock DO WAIT h.psWait ENDLOOP;
h.psLock ← TRUE;
};
UnlockBackingStore: ENTRY PROC [h: RoseDisplay] = {
h.psLock ← FALSE;
BROADCAST h.psWait;
};
BitsAndParts: PROC [handle: RoseDisplay, roseValues: Rosemary.RoseValues] RETURNS [bits: Ports.LevelSequence, parts: GraphValueSequence, size: NAT ← 0] = {
partCount: NAT ← 0;
fetchValues: HashTable.Table ← HashTable.Create[];
LockBackingStore[handle];
FOR vals: Rosemary.RoseValues ← roseValues, vals.rest UNTIL vals=NIL DO
graphData: GraphData ← NARROW[vals.first.roseWire.data];
size ← size + vals.first.fieldWidth;
IF graphData=NIL THEN vals.first.roseWire.data ← graphData ← NEW[GraphDataRec];
partCount ← partCount + 1;
graphData.interested ← TRUE;
IF graphData.first=NIL AND graphData.last#NIL THEN IF NOT HashTable.Insert[fetchValues, graphData, NIL] THEN ERROR;
ENDLOOP;
IF HashTable.GetSize[fetchValues]>0 THEN {
ReadLong: PROC RETURNS [l: bytes Basics.LongNumber] = {
l.lh ← LOOPHOLE[IO.GetChar[handle.ps]];
l.ll ← LOOPHOLE[IO.GetChar[handle.ps]];
l.hh ← LOOPHOLE[IO.GetChar[handle.ps]];
l.hl ← LOOPHOLE[IO.GetChar[handle.ps]];
};
index: INTIO.GetIndex[handle.ps];
IO.SetIndex[handle.ps, 0];
TRUSTED {
UNTIL IO.GetIndex[handle.ps]=index DO
head: GraphData ← NARROW[HashTable.Fetch[handle.psHash, LOOPHOLE[ReadLong[]]].value];
size: CARDINAL ← head.last.size;
found: BOOL;
lastRef: REF ANY;
[found, lastRef] ← HashTable.Fetch[fetchValues, head];
IF found THEN {
last: GraphValue ← NARROW[lastRef];
new: GraphValue ← NEW[GraphValueRec[size]];
new.time ← LOOPHOLE[ReadLong[]];
FOR i: CARDINAL IN [0..size) DO
new.value[i] ← LOOPHOLE[IO.GetChar[handle.ps]];
ENDLOOP;
IF last=NIL THEN {
head.first ← new;
new.next ← SELECT TRUE FROM
head.secondToLast#NIL => head.secondToLast,
head.nextToLast#NIL => head.nextToLast,
ENDCASE => head.last;
}
ELSE {
new.next ← last.next;
last.next ← new;
};
IF NOT HashTable.Replace[fetchValues, head, new] THEN ERROR;
}
ELSE {
[] ← ReadLong[];
FOR i: CARDINAL IN [0..size) DO
[] ← IO.GetChar[handle.ps];
ENDLOOP;
};
ENDLOOP;
};
};
UnlockBackingStore[handle];
bits ← NEW[Ports.LevelSequenceRec[size]];
parts ← NEW[GraphValueSequenceRec[partCount]];
};
InitBitsAndParts: PROC [roseValues: Rosemary.RoseValues, bits: Ports.LevelSequence, parts: GraphValueSequence, bound: REAL] RETURNS [lastTime: INT ← 0] = {
EnumValues: PROC [valueExpr: PROC [graphData: GraphData] RETURNS [value: GraphValue]] RETURNS [quit: BOOLFALSE] = {
firstFreeBit: NAT ← 0;
partCount: NAT ← 0;
FOR vals: Rosemary.RoseValues ← roseValues, vals.rest UNTIL vals=NIL DO
graphData: GraphData ← NARROW[vals.first.roseWire.data];
value: GraphValue ← valueExpr[graphData];
IF value=NIL THEN RETURN [TRUE];
parts[partCount] ← value;
partCount ← partCount + 1;
FOR bit: NAT IN [0..value.size) DO
bits[firstFreeBit+bit] ← value.value[bit];
ENDLOOP;
firstFreeBit ← firstFreeBit + value.size;
ENDLOOP;
};
BoundValue: PROC [graphData: GraphData] RETURNS [value: GraphValue ← NIL] = {
CheckValue: PROC [check: GraphValue] = {
IF check#NIL AND check.time<=bound THEN {
value ← check;
lastTime ← MAX[lastTime, check.time];
};
};
IF graphData#NIL THEN {
CheckValue[graphData.last];
IF value=NIL THEN CheckValue[graphData.nextToLast];
IF value=NIL THEN CheckValue[graphData.secondToLast];
};
};
UnboundValue: PROC [graphData: GraphData] RETURNS [value: GraphValue] = {
value ← IF graphData=NIL THEN NIL ELSE graphData.first;
};
IF EnumValues[BoundValue] THEN {
lastTime ← 0;
IF EnumValues[UnboundValue] THEN lastTime ← -1;
};
};
WireTimeValue: PUBLIC PROC [handle: RoseDisplay, flatWire: CoreFlat.FlatWire, time: INT] RETURNS [value: Ports.LevelSequence] = {
simulation: Rosemary.Simulation ← handle.simulation;
roseValues: Rosemary.RoseValues ← Rosemary.GetValues[simulation, flatWire];
parts: GraphValueSequence;
partCount: NAT ← 0;
[value, parts] ← BitsAndParts[handle, roseValues];
partCount ← parts.size;
IF InitBitsAndParts[roseValues, value, parts, time]>=0 THEN DO
firstBit: NAT ← 0;
minTime: INTLAST[INT];
FOR v: NAT IN [0..partCount) DO
IF parts[v].next#NIL THEN minTime ← MIN[minTime, parts[v].next.time];
ENDLOOP;
IF minTime > time THEN EXIT;
FOR v: NAT IN [0..partCount) DO
IF parts[v].next#NIL AND parts[v].next.time=minTime THEN {
parts[v] ← parts[v].next;
FOR bit: NAT IN [0..parts[v].size) DO
value[firstBit+bit] ← parts[v].value[bit];
ENDLOOP;
};
firstBit ← firstBit + parts[v].size;
ENDLOOP;
ENDLOOP;
};
RoseGraphEnumerate: PlotGraph.GraphEnumerateProc = {
[plot: Plot, graph: Graph, bounds: Rectangle, eachPoint: PointProc, data: REF ANY]
WritePoint: PROC [time: INT] RETURNS [quit: BOOL] = {
quit ← IF analog THEN eachPoint[
Real.Float[time],
SELECT bits[0] FROM
L => 0.0,
X => 2.5,
H => 5.0,
ENDCASE => ERROR,
data]
ELSE eachPoint[x: Real.Float[time], y:0.0, data: data, rope: Ports.LSToRope[bits]];
};
handle: RoseDisplay ← NARROW[plot.data];
graphPlotData: GraphPlotData ← NARROW[graph.data];
roseValues: Rosemary.RoseValues ← graphPlotData.roseValues;
parts: GraphValueSequence ← graphPlotData.parts;
bits: Ports.LevelSequence ← graphPlotData.bits;
analog: BOOL ← roseValues.rest=NIL AND roseValues.first.fieldWidth=1;
partCount: NAT ← parts.size;
lastTime: INT ← InitBitsAndParts[roseValues, bits, parts, bounds.x];
IF lastTime>=0 AND NOT WritePoint[lastTime] THEN {
quit: BOOLFALSE;
minTime: INT;
DO
firstBit: NAT ← 0;
minTime ← LAST[INT];
FOR v: NAT IN [0..partCount) DO
IF parts[v].next#NIL THEN minTime ← MIN[minTime, parts[v].next.time];
ENDLOOP;
IF minTime > handle.lastValidTime THEN EXIT;
IF analog THEN IF (quit ← WritePoint[minTime]) THEN EXIT;
FOR v: NAT IN [0..partCount) DO
IF parts[v].next#NIL AND parts[v].next.time=minTime THEN {
parts[v] ← parts[v].next;
FOR bit: NAT IN [0..parts[v].size) DO
bits[firstBit+bit] ← parts[v].value[bit];
ENDLOOP;
};
firstBit ← firstBit + parts[v].size;
ENDLOOP;
lastTime ← minTime;
IF (quit ← WritePoint[lastTime]) THEN EXIT;
ENDLOOP;
IF (NOT quit) AND lastTime < handle.lastValidTime THEN [] ← WritePoint[handle.lastValidTime];
};
};
END.