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:
ROPE ←
NIL, 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:
ROPE ←
NIL, displayWires: CoreFlat.FlatWires ←
NIL, graphWires: CoreFlat.FlatWires ←
NIL, cutSet: CoreFlat.CutSet ←
NIL, historySize:
NAT ← 0, steady:
BOOL ←
FALSE, recordDeltas:
BOOL ←
TRUE]
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:
BOOL ←
TRUE] = {
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: NAT ← IF 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: BOOL ← FALSE;
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:
ROPE ←
NIL ] = {
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.STREAM ← IO.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.STREAM ← IO.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:
BOOL ←
TRUE, quit:
BOOL ←
FALSE] = {
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"];
};
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.
ROPE ←
NIL] = {
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:
BOOL ←
FALSE] = {
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: BOOL ← FALSE];
GraphValue: TYPE = REF GraphValueRec;
GraphValueRec:
TYPE =
RECORD [
next: GraphValue ← NIL,
time: INT ← LAST[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: NAT ← IF single THEN 1 ELSE wire.currentValue.size;
new: BOOL ← FALSE;
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: BOOL ← FALSE;
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: INT ← IO.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:
BOOL ←
FALSE] = {
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: INT ← 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 > 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: BOOL ← FALSE;
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.