-- FILE: PrintoutImpl.mesa
-- Last edited by Ousterhout, August 30, 1983 1:40 pm

DIRECTORY
    Globals,
    Hash,
    IO,
    Model,
    Parse,
    Printout,
    Rope;

PrintoutImpl: CEDAR PROGRAM
IMPORTS
    Globals,
    Hash,
    IO,
    Model,
    Rope,
    Parse
EXPORTS Printout =
BEGIN
OPEN Printout, Globals;

Units: PUBLIC REAL ← 2.0;

-- The following variables are used to communicate between command-level
-- routines and enumeration action routines.

dupTable: Hash.Table;
threshold: REAL;
nDups: INT;


FetRope: PUBLIC PROC[fet: Fet] RETURNS [rope: Rope.ROPE] =

    BEGIN
    rope ← IO.PutFR["g=%s, s=%s, d=%s, loc=%f,%f",
        IO.rope[fet.gate.name],
        IO.rope[fet.source.name],
        IO.rope[fet.drain.name],
        IO.real[fet.x/Units],
        IO.real[fet.y/Units]];
    RETURN [rope];
    END;


NodeRope: PUBLIC PROC[node: Node] RETURNS [rope: Rope.ROPE] =

    BEGIN
    x,y: REAL;
    p: Pointer;
    terminal: Rope.ROPE;
    f: Fet;
    
    -- The only tricky thing about this routine is that nodes
    -- don't contain (x,y) locations.  I've found that the best
    -- way to identify a node is with a transistor gate.  So, this
    -- procedure checks the first few transistors attached to the
    -- node to see if there's a gate available for use as a reference
    -- point.
    
    p ← node.firstPointer;
    IF p = NIL THEN
        BEGIN
        RETURN [node.name];
        END
    ELSE
        BEGIN
        FOR i:INT IN [0..10) DO
            f ← p.fet;
            IF node = f.gate THEN EXIT;
            IF p.next = NIL THEN EXIT;
            p ← p.next;
            ENDLOOP;
        x ← f.x/Units;
        y ← f.y/Units;
        IF node = f.gate THEN terminal ← "gate"
        ELSE IF node = f.drain THEN terminal ← "drain"
        ELSE IF node = f.source THEN terminal ← "source"
        ELSE
            BEGIN
            IO.PutF[StdOut, "Crystal bug: node %s doesn't connect right!\n",
                IO.rope[node.name]];
            RETURN ["??"];
            END;
        rope ← IO.PutFR["%s (see %s at %f,%f)",
            IO.rope[node.name],
            IO.rope[terminal],
            IO.real[x],
            IO.real[y]];
        RETURN [rope];
        END;
    END;


PrintCap: PUBLIC CmdProc =

    BEGIN
    ParseOK: BOOLEAN;
    
    dupTable ← Hash.NewTable[];
    nDups ← 0;
    threshold ← 0.0;
    
    -- Read off switches, if there are any.
    
    WHILE args # NIL DO
        IF Rope.Fetch[args.rope, 0] # '- THEN EXIT;
        IF Rope.Length[args.rope] # 2 THEN
            BEGIN
            IO.PutF[StdOut, "Bad switch: %s\n", IO.rope[args.rope]];
            args ← args.next;
            LOOP;
            END;
        SELECT Rope.Fetch[args.rope, 1] FROM
            't =>
                BEGIN
                [ParseOK, threshold] ← Parse.Real[args.next];
                IF ParseOK THEN args ← args.next
                ELSE IO.PutF[StdOut, "No threshold value given, using 0.\n"];
                END;
            ENDCASE => IO.PutF[StdOut, "Bad switch: %s\n", IO.rope[args.rope]];
        args ← args.next;
        ENDLOOP;
    
    -- Read off nodes.  For each node, look it up in the node table and print
    -- out its capacitance if it's greater than threshold.
    
    nDups ← 0;
    IF args = NIL THEN
        Hash.Enumerate[table: NodeTable, pattern: "*", proc: capProc,
            errorStream: StdOut]
    ELSE WHILE args # NIL DO
        Hash.Enumerate[table: NodeTable, pattern: args.rope,
            proc: capProc, errorStream: StdOut];
        args ← args.next;
        ENDLOOP;
    IF nDups > 0 THEN
        IO.PutF[StdOut, "%d duplicates not printed.\n", IO.int[nDups]];
    END;
 
capProc: Hash.EnumProc =
    BEGIN
    valRope: Rope.ROPE;
    node: Node;
     
    node ← NARROW[entry.clientData];
    IF node = NIL THEN RETURN;
    IF node.cap >= threshold THEN
        BEGIN
        valRope ← IO.PutFR["%.3f", IO.real[node.cap]];
        entry ← Hash.Find[dupTable, valRope];
        IF (entry.clientData # $Duplicate) OR DupsOK THEN
            BEGIN
            entry.clientData ← $Duplicate;
            IO.PutF[StdOut, "%s pf capacitance at %s.\n",
                IO.rope[valRope],
                IO.rope[NodeRope[node]]];
            END
        ELSE nDups ← nDups + 1;
        END;
    END;


PrintRes: PUBLIC CmdProc =

    BEGIN
    ParseOK: BOOLEAN;
    
    dupTable ← Hash.NewTable[];
    nDups ← 0;
    threshold ← 0.0;
    
    -- Read off switches, if there are any.
    
    WHILE args # NIL DO
        IF Rope.Fetch[args.rope, 0] # '- THEN EXIT;
        IF Rope.Length[args.rope] # 2 THEN
            BEGIN
            IO.PutF[StdOut, "Bad switch: %s\n", IO.rope[args.rope]];
            args ← args.next;
            LOOP;
            END;
        SELECT Rope.Fetch[args.rope, 1] FROM
            't =>
                BEGIN
                [ParseOK, threshold] ← Parse.Real[args.next];
                IF ParseOK THEN args ← args.next
                ELSE IO.PutF[StdOut, "No threshold value given, using 0.\n"];
                END;
            ENDCASE => IO.PutF[StdOut, "Bad switch: %s\n", IO.rope[args.rope]];
        args ← args.next;
        ENDLOOP;
    
    -- Read off nodes.  For each node, look it up in the node table and print
    -- out its resistance if it's greater than threshold.
    
    nDups ← 0;
    IF args = NIL THEN
        Hash.Enumerate[table: NodeTable, pattern: "*", proc: resProc,
            errorStream: StdOut]
    ELSE WHILE args # NIL DO
        Hash.Enumerate[table: NodeTable, pattern: args.rope,
            proc: resProc, errorStream: StdOut];
        args ← args.next;
        ENDLOOP;
    IF nDups > 0 THEN
        IO.PutF[StdOut, "%d duplicates not printed.\n", IO.int[nDups]];
    END;
 
resProc: Hash.EnumProc =
    BEGIN
    valRope: Rope.ROPE;
    node: Node;
     
    node ← NARROW[entry.clientData];
    IF node = NIL THEN RETURN;
    IF node.res >= threshold THEN
        BEGIN
        valRope ← IO.PutFR["%.1f", IO.real[node.res]];
        entry ← Hash.Find[dupTable, valRope];
        IF (entry.clientData # $Duplicate) OR DupsOK THEN
            BEGIN
            entry.clientData ← $Duplicate;
            IO.PutF[StdOut, "%s ohms resistance at %s.\n",
                IO.rope[valRope],
                IO.rope[NodeRope[node]]];
            END
        ELSE nDups ← nDups + 1;
        END;
    END;


PrintFets: PUBLIC CmdProc =
    BEGIN
    IF args = NIL THEN Hash.Enumerate[table: NodeTable, pattern: "*", proc: fetProc,
        errorStream: StdOut]
    ELSE WHILE args # NIL DO
        Hash.Enumerate[table: NodeTable, pattern: args.rope,
            proc: fetProc, errorStream: StdOut];
        args ← args.next;
        ENDLOOP;
    END;

fetProc: Hash.EnumProc =
    BEGIN
    node: Node;
    pointer: Pointer;
    fet: Fet;
    fp: FlowPtr;
    
    node ← NARROW[entry.clientData];
    FOR pointer ← node.firstPointer, pointer.next UNTIL pointer = NIL DO
        fet ← pointer.fet;
        IF fet.gate = node THEN
            BEGIN
            IO.PutF[StdOut, "%s, type=%s, aspect=%.3f, area=%.3f\n",
                IO.rope[FetRope[fet]],
                IO.rope[Model.TypeTable[fet.type].name],
                IO.real[fet.aspect],
                IO.real[fet.area/(Units*Units)]];
            IO.PutRope[StdOut, "    Flags:"];
            IF fet.flowFromSource THEN IO.PutRope[StdOut, " fromSource"];
            IF fet.flowFromDrain THEN IO.PutRope[StdOut, " fromDrain"];
            IF fet.forcedOn THEN IO.PutRope[StdOut, " forcedOn"];
            IF fet.forcedOff THEN IO.PutRope[StdOut, " forcedOff"];
            IF fet.on0 THEN IO.PutRope[StdOut, " on0"];
            IF fet.on1 THEN IO.PutRope[StdOut, " on1"];
            IF fet.onAlways THEN IO.PutRope[StdOut, " onAlways"];
            IF fet.noSourceInfo THEN IO.PutRope[StdOut, " noSourceInfo"];
            IF fet.noDrainInfo THEN IO.PutRope[StdOut, " noDrainInfo"];
            IF fet.firstFlow # NIL THEN IO.PutRope[StdOut, " Flow:"];
            FOR fp ← fet.firstFlow, fp.next UNTIL fp = NIL DO
                IF fp.source THEN IO.PutF[StdOut, " s=%s", IO.rope[fp.flow.name]];
                IF fp.drain THEN IO.PutF[StdOut, " d=%s", IO.rope[fp.flow.name]];
                ENDLOOP;
            IO.PutRope[StdOut, "\n"];
            END;
        ENDLOOP;
    END;


PrintNodes: PUBLIC CmdProc =
    BEGIN
    IF args = NIL THEN Hash.Enumerate[table: NodeTable, pattern: "*",
        proc: nodeProc, errorStream: StdOut]
    ELSE WHILE args # NIL DO
        Hash.Enumerate[table: NodeTable, pattern: args.rope,
            proc: nodeProc, errorStream: StdOut];
        args ← args.next;
        ENDLOOP;
    END;

nodeProc: Hash.EnumProc =
    BEGIN
    node: Node ← NARROW[entry.clientData];
    IO.PutF[StdOut, "%s:", IO.rope[node.name]];
    IF node.always0 THEN IO.PutRope[StdOut, " always 0"];
    IF node.always1 THEN IO.PutRope[StdOut, " always 1"];
    IF node.input THEN IO.PutRope[StdOut, " input"];
    IF node.output THEN IO.PutRope[StdOut, " output"];
    IF node.precharged THEN IO.PutRope[StdOut, " precharged"];
    IF node.bus THEN IO.PutRope[StdOut, " bus"];
    IF node.dynamic THEN IO.PutRope[StdOut, " dynamic"];
    IF node.inPath THEN IO.PutRope[StdOut, " inPath"];
    IF node.ratioError THEN IO.PutRope[StdOut, " ratioError"];
    IF node.watched THEN IO.PutRope[StdOut, " watched"];
    IO.PutF[StdOut, "\n    capacitance = %.5f, resistance = %.1f,",
        IO.real[node.cap], IO.real[node.res]];
    IO.PutF[StdOut, "\n    rise time = %.1f, fall time = %.1f\n",
        IO.real[node.hiTime], IO.real[node.loTime]];
    END;
END.