-- SimNsim.mesa
-- last edited by Suzuki:  December 16, 1981  2:59 PM

DIRECTORY
  ComParse,
  InlineDefs,
  IODefs USING [CR, Rubout, TAB],
  MOSSIMFns USING [UnderJaM],
  SimStep,
  SimStorage,
  SimTsim,
  SimTTable,
  stdio,
  StdioImpl,
  StreamDefs,
  StringDefs,
  SystemDefs,
  TimeDefs,
  WF,
  WFRealImpl;

SimNsim: PROGRAM 
  IMPORTS ComParse, InlineDefs, IODefs, MOSSIMFns, SimStep, SimStorage, SimTsim, SimTTable, stdio, StdioImpl, StreamDefs, StringDefs, SystemDefs, TimeDefs, WF, WFRealImpl
  EXPORTS MOSSIMFns, SimTsim = { OPEN SimStep, stdio, WF;

-- #include "tsim.h"

-- event driven mosfet simulator. Chris Terman (2/80) 

HASHSIZE: CARDINAL = 731;
MAXBITS: CARDINAL = 50;

-- electrical parameters used for deriving capacitance info for charge sharing 
--NSIL-II numbers  J. Cherry
CAPMA: REAL =  3.2e-9;   -- metal capacitance, pf/sq-centimicron 
CAPPA: REAL =  4e-9;   -- poly capacitance, pf/sq-centimicron 
CAPDA: REAL =  9.6e-9;   -- diffusion capacitance, pf/sq-centimicron 
CAPDP: REAL =  2.4e-6;   -- diffusion perimeter capacitance, pf/centimicron 
CAPGA: REAL =  30e-9;   -- gate capacitance, pf/sq-centimicron 

LAMBDA: REAL =  2.5;     -- microns/lambda 
LAMBDA2: REAL = LAMBDA*LAMBDA;

LSIZE: CARDINAL =  500;
NFREE: CARDINAL =  2048;

wlist: SimTsim.wptr ← NIL;

bptr: TYPE = POINTER TO Bits;

Bits: TYPE = RECORD[
  bnext: bptr,    -- next bit vector in chain 
  bnbits: CARDINAL,    -- number of bits in this vector 
  bbits: ARRAY [0..MAXBITS) OF SimTsim.nptr,  -- pointers to the bits (nodes) 
  bname: STRING    -- name of this vector of bits 
];

LINELENGTH: CARDINAL = 80;   -- line length by CMB 

GNDNode: PUBLIC SimTsim.nptr;
VDDNode,PHI1Node,PHI2Node: SimTsim.nptr;

debug: PUBLIC INTEGER ← 0;    -- <>0 if debugging 
curpos: INTEGER ← 0;    -- current output column number 

ntrans: CARDINAL ← 0; -- number of transistors
nion: INTEGER ← 0;    -- number of depletion mode transistors 
nindex: INTEGER ← 0;    -- index for nodeArray 
tindex: INTEGER ← 0;    -- index for transArray 

blist: bptr ← NIL;  -- list of watched bit arrays 

hinputs: PUBLIC ARRAY [0..SimTsim.NINPUTS) OF SimTsim.nptr;  -- list of nodes to be driven high 
nhinputs: PUBLIC INTEGER ← 0;  -- number of entries in hinputs 
linputs: PUBLIC ARRAY [0..SimTsim.NINPUTS) OF SimTsim.nptr;  -- list of nodes to be driven low 
nlinputs: PUBLIC INTEGER ← 0;  -- number of entries in linputs 
xinputs: PUBLIC ARRAY [0..SimTsim.NINPUTS) OF SimTsim.nptr;  -- list of nodes just removed from input lists 
nxinputs: PUBLIC INTEGER ← 0;  -- number of entries in xinputs 

lineno: INTEGER ← 0;    -- current line number 

targv: PUBLIC ARRAY [0..SimTsim.MaxTargv) OF STRING;  -- pointer to tokens on current command line 
targc: PUBLIC CARDINAL ← 0;    -- number of args on current command line 

number: STRING ← [50];  -- Declared static in pnode

filename: STRING;    -- current input file 
outfile: PUBLIC FILE;  -- current output file 
sfile: FILE;    -- current state file 

nfree: INTEGER ← 0;    -- number of bytes of free storage remaining 

hash: POINTER TO ARRAY [0..HASHSIZE) OF SimTsim.nptr ← @hashBody;
hashBody: ARRAY [0..HASHSIZE) OF SimTsim.nptr;

uhash: POINTER TO ARRAY [0..HASHSIZE) OF SimTsim.uptr ← @uhashBody;
uhashBody: ARRAY [0..HASHSIZE) OF SimTsim.uptr;

-- JaM communication

JaMResult: STRING;
JaMError: PUBLIC STRING;

-- MOSSIM.log

log: PUBLIC StreamDefs.DiskHandle ← NIL;
traceNumber: PUBLIC CARDINAL ← 0;

-- Procedures

hashcode: PROC[name: STRING] RETURNS [CARDINAL] =
  INLINE {  i: CARDINAL ← 0;
  j: CARDINAL;
  char: CHARACTER;

  FOR j IN [0..name.length) DO
    char ← name[j];
    i ← InlineDefs.BITSHIFT[i,4] + 
      (IF 141C<=char THEN (char - 120C) ELSE (char - '0));
  ENDLOOP;
  RETURN[i MOD HASHSIZE];
};

find: PROC[name: STRING] RETURNS[SimTsim.nptr] = 
  {  ntemp: SimTsim.nptr;
  num: CARDINAL ← numberp[name];

  FOR ntemp ← hash[hashcode[name]], ntemp.hnext WHILE ntemp#NIL DO
    IF NOT ntemp.named 
      THEN IF num=LOOPHOLE[ntemp.nname, CARDINAL] THEN RETURN[ntemp]
      ELSE LOOP
    ELSE IF StringDefs.EquivalentString[name, ntemp.nname] 
      THEN RETURN[ntemp];
  ENDLOOP;
  RETURN[NIL];
};

ufind: PROC[name: STRING] RETURNS [SimTsim.nptr] =
  {  utemp: SimTsim.uptr;

  FOR utemp ← uhash[hashcode[name]], utemp.unext WHILE utemp#NIL DO
    IF StringDefs.EquivalentString[name, utemp.uname] THEN RETURN[utemp.unode];
  ENDLOOP;
  RETURN[find[name]];
};

-- visit each node in network, calling function passed as arg with current node 
walkNet: PUBLIC PROC[func: PROC[SimTsim.nptr] RETURNS[CARDINAL]] RETURNS[CARDINAL] =
  {  index: INTEGER;
  n: SimTsim.nptr;
  total: CARDINAL ← 0;

  FOR index ← 0, index+1 WHILE index<HASHSIZE DO
    FOR n ← hash[index], n.hnext UNTIL n=NIL DO
            total ← total+func[n]
          ENDLOOP;
        ENDLOOP;
  RETURN[total];
};

-- see if asciz string is an integer if so, return it; otherwise return 0 
numberp: PROC[s: STRING] RETURNS [CARDINAL] =
  {  num: CARDINAL ← 0;
  c, i, temp: CARDINAL;

  FOR i IN [0..s.length) DO 
     IF (c ← s[i] - '0) IN [0..9] THEN {
      temp ← num*10 + c;
      IF temp<num THEN RETURN[0]
      ELSE num ← temp}
     ELSE RETURN[0]
  ENDLOOP;
  RETURN[num];
};

-- get node structure  flag<>0 if ok to create new one 
getnode: PROC [p: STRING] RETURNS [SimTsim.nptr] =
 {  n: SimTsim.nptr;
  f: INTEGER;

  IF (n ← find[p])#NIL THEN RETURN[n];

  -- allocate new node from free storage 
  n ← SimStorage.AllocateNode[];

  nindex ← nindex + 1;

  -- initialize node entries 
  n.ngate ← n.nsource ← n.ndrain ← NIL;
  n.nlink ← NIL;
  n.ncap ← 0.0;
  n.marked ← n.xqueued ← n.named ← n.pullup ← n.input ← n.watched ← n.traced←n.warned ← FALSE;
  n.npot ← CX;
  n.changecount ← 0;
  n.stepcount ← 0;

  n.fpot ← NotFX;
  n.elink ← NIL;
  n.ndef ← n.nfuns ← NIL;

  f ← hashcode[p];
  n.hnext ← hash[f];
  hash[f] ← n;

  -- if node name is just a number, store it as such 
  IF (f ← numberp[p])#0 THEN {
    n.nname ← LOOPHOLE[f, STRING];
    n.named ← FALSE }
  ELSE {
    n.nname ← SystemDefs.AllocateHeapString[p.length]; -- Released by the main
    StringDefs.AppendString[n.nname, p];
    n.named ← TRUE;
  };

  RETURN[n];
};

-- return pointer to asciz name of node 
pnode: PUBLIC PROC[n: SimTsim.nptr] RETURNS [STRING] =
  {  liPrint: CARDINAL ← LOOPHOLE[n.nname, CARDINAL];
  IF n.named THEN RETURN[n.nname];
  number.length ← 0;
  SWF1[number,"%u"L,liPrint];
  RETURN[number];
};

-- node area and perimeter info 
nodeInfoString: PUBLIC PROC =
  {  n: SimTsim.nptr;

  JaMError.length ← JaMResult.length ← 0;

  IF targc # 8 THEN {error2String["Bad node record"L, "*n"L]; RETURN};

  n ← getnode[targv[1]];

  n.ncap ← n.ncap + atof[targv[4]] * (CAPMA*LAMBDA2) +
       atof[targv[5]] * (CAPPA*LAMBDA2) +
       atof[targv[6]] * (CAPDA*LAMBDA2) +
       atof[targv[7]] * 2.0 * (CAPDP*LAMBDA2);

  n.xpos ← atof[targv[2]];
  n.ypos ← atof[targv[3]];
};

potential: PUBLIC ARRAY SimTsim.Potential OF STRING ← [
  "initial",
  "driven unknown",
  "driven high",
  "driven low",
  "charged unknown",
  "charged high",
  "charged low",
  "pulled up",
  "unknown-low",
  "unknown-high",
  "shared charge"
];

newtransString: PROC[flag: CARDINAL] = 
-- flag=0 => enhancement mode transistor.  flag=1 => depletion...
  {  node1,node2,node3,ntemp: SimTsim.nptr;
  capacitance,resistance: REAL;
  t: SimTsim.tptr;

  ntrans ← ntrans+1; -- the only place ntrans is increased
  IF ntrans MOD 100 = 0 THEN {
    IF ntrans MOD 1000 = 0 THEN {
      IF ntrans MOD 10000 = 0 THEN WF.WF1["%u"L, ntrans]
      ELSE putc['!, outfile]}
    ELSE putc['., outfile]};
  JaMError.length ← JaMResult.length ← 0;

  IF (targc<4 OR targc>10) THEN {
    error2String["Bad transistor record"L,"*n"L]; RETURN};

  node1 ← getnode[targv[1]];
  node2 ← getnode[targv[2]];
  node3 ← getnode[targv[3]];
  
  IF targc = 8 THEN {
    capacitance ← atof[targv[6]]*atof[targv[7]] * (CAPGA*LAMBDA2);
-- #ifdef TIMING
--    resistance ← (atof[targv[4]]/atof[targv[5]]) * RESG;
-- #endif
  } ELSE {
    capacitance ← 0.0;
    resistance ← 0.01;
  };

  IF (node2=VDDNode OR node2=GNDNode) THEN
    { ntemp ← node2; node2 ← node3; node3 ← ntemp; };

  IF flag#0 AND (node3 = VDDNode) THEN {
    node2.pullup ← TRUE;

    node2.ncap ← node2.ncap + capacitance;
    nion ← nion+1;
    RETURN;
  };

  -- allocate new transistor from free storage 
  t ← SimStorage.AllocateTrans[];
  tindex ← tindex + 1;

  IF flag#0 THEN {  -- yellow transistor 
    node1.ncap ← node1.ncap + capacitance;
    node1 ← VDDNode;
  };

  t.gate ← node1;
  t.glink ← node1.ngate; node1.ngate ← t;
  t.source ← node2;
  t.slink ← node2.nsource; node2.nsource ←t;
  t.drain ← node3;
  t.dlink ← node3.ndrain; node3.ndrain ← t;

  node1.ncap ← node1.ncap + capacitance;
};

intvalue: ARRAY SimTsim.Potential OF INTEGER = [ 4, 2, 1, 0, 2, 1, 0, 1, 2, 2, 3 ];

pNode: PROC[name: STRING, n:SimTsim.nptr] =
  {  IF NOT n.watched THEN RETURN;
  curpos ← curpos + name.length + 2 + (IF debug#0 THEN potential[n.npot].length ELSE 1);
  IF curpos > LINELENGTH THEN {
    curpos ← name.length + 2 + (IF debug#0 THEN potential[n.npot].length ELSE 1);
    FWF0[outfile,"*n"L];
  };
  IF debug=0 THEN FWF2[outfile,"%s=%c "L, name, SimTsim.pchars[n.npot]]
  ELSE FWF2[outfile,"%s=%s "L, name, potential[n.npot]];
};

pnlist: PROC =
  {  b: bptr ← blist;
  w: SimTsim.wptr;
  ch: CHARACTER;
  f,vok: CARDINAL;
  val: InlineDefs.LongNumber;

  WHILE b#NIL DO
    FWF1[outfile,"%s="L,b.bname];
    val.lc ← 0;
    vok ← 1;
    FOR f IN [0..b.bnbits) DO
      ch ← SimTsim.pchars[b.bbits[f].npot];
      FWF1[outfile,"%c"L,ch];
      val.highbits ← InlineDefs.BITSHIFT[val.highbits, 1];
      IF LOOPHOLE[val.lowbits, INTEGER]<0 THEN
        val.highbits ← InlineDefs.BITOR[val.highbits, 1];
      val.lowbits ← InlineDefs.BITSHIFT[val.lowbits, 1];
      IF (ch = '1)
        THEN val.lowbits ← InlineDefs.BITOR[val.lowbits,1]
      ELSE IF (ch = 'X) THEN vok ← 0;
    ENDLOOP;
    IF (vok) # 0 THEN FWF1[outfile,"*t%lu"L,@(val.lc)];
    FWF0[outfile,"*n"L];
    b ← b.bnext;
  ENDLOOP;

  curpos ← 0;
  FOR w ← wlist, w.wnext UNTIL w=NIL DO 
    pNode[w.wname,ufind[w.wname]] 
  ENDLOOP;
  IF (curpos # 0) THEN FWF0[outfile,"*n"L];
  fflush[outfile];
};

vectorValue: PUBLIC PROC[] RETURNS [STRING] = {
  b: bptr ← blist;
  ch: CHARACTER;
  f: CARDINAL;
  name: STRING ← targv[1];
  JaMResult.length ← JaMError.length ← 0;
  FOR b ← blist, b.bnext UNTIL b=NIL DO
    IF StringDefs.EquivalentString[b.bname, name] THEN
      {FOR f ← 0, f+1 UNTIL f>=b.bnbits DO
        ch ← SimTsim.pchars[b.bbits[f].npot];
        JaMResult[f] ← ch;
      REPEAT
        FINISHED => JaMResult.length ← f;
      ENDLOOP;
      EXIT};
  REPEAT
    FINISHED => {SWF1[JaMError, "This name is undefined: %s"L, name]};
  ENDLOOP;
  RETURN[JaMResult]};

putVectorValue: PUBLIC PROC[arg: LONG INTEGER] = {
  b: bptr ← blist;
  f: CARDINAL;
  name: STRING ← targv[1];
  JaMResult.length ← JaMError.length ← 0;
  FOR b ← blist, b.bnext UNTIL b=NIL DO
    IF StringDefs.EquivalentString[b.bname, name] THEN {
      THROUGH [0..MAXBITS-b.bnbits) DO arg ← arg*2 ENDLOOP;
      FOR f ← 0, f+1 UNTIL f>=b.bnbits DO
        setin[b.bbits[f], IF arg<0 THEN 'h ELSE 'l];
        arg ← arg*2;
      ENDLOOP;
    };
  REPEAT
    FINISHED => {SWF1[JaMError, "This name is undefined: %s"L, name]};
  ENDLOOP;
};

pvalue: PUBLIC PROC[n: SimTsim.nptr] =
  {  IF debug=0 THEN FWF2[outfile,"%s=%c "L, pnode[n], SimTsim.pchars[n.npot]]
  ELSE FWF2[outfile,"%s=%s "L,pnode[n], potential[n.npot]];
};

ptrans: PUBLIC PROC[t: SimTsim.tptr] =
  {  FWF0[outfile,"trans "L];
  pvalue[t.gate];
  pvalue[t.source];
  pvalue[t.drain];
  FWF0[outfile,"*n"L];
};

idelete: PROC[n: SimTsim.nptr, list: POINTER TO ARRAY OF SimTsim.nptr, cnt: POINTER TO INTEGER] =
  {  i:INTEGER ← cnt↑;
  j, k: INTEGER;

  FOR j IN [0..i) DO
    IF list[j] = n THEN {
    FOR k IN [j..i-1) DO
      list[k] ← list[k+1]
    ENDLOOP; 
      cnt↑ ← cnt↑ - 1;
      RETURN;
    };
  ENDLOOP;
};

iinsert: PROC[n: SimTsim.nptr, list: POINTER TO ARRAY OF SimTsim.nptr, cnt: POINTER TO INTEGER] =
  {  i: INTEGER ← cnt↑;
  j:INTEGER;

  FOR j IN [0..i) DO
    IF list[j] = n THEN RETURN;
  ENDLOOP;
  IF (cnt↑ ← i + 1) > SimTsim.NINPUTS THEN
    { error2String["Too many inputs"L,"*n"L]; cnt↑ ← i; RETURN; };
  list[i] ← n
};

setin: PROC[n: SimTsim.nptr, which: CHARACTER] =
  {  idelete[n,LOOPHOLE[@hinputs, POINTER TO ARRAY OF SimTsim.nptr],@nhinputs];
  idelete[n,LOOPHOLE[@linputs, POINTER TO ARRAY OF SimTsim.nptr],@nlinputs];
  idelete[n,LOOPHOLE[@xinputs, POINTER TO ARRAY OF SimTsim.nptr],@nxinputs];
  SELECT which FROM
    'h => {  iinsert[n,LOOPHOLE[@hinputs, POINTER TO ARRAY OF SimTsim.nptr],@nhinputs];
      n.input ← TRUE};

    'l => {  iinsert[n,LOOPHOLE[@linputs, POINTER TO ARRAY OF SimTsim.nptr],@nlinputs];
      n.input ← TRUE;};

    'x => {  iinsert[n,LOOPHOLE[@xinputs, POINTER TO ARRAY OF SimTsim.nptr],@nxinputs];
      n.input ← FALSE};
  ENDCASE;
};

setInputString: PROC[which: CHARACTER] =
  {  n: SimTsim.nptr;
  i:CARDINAL;

  JaMError.length ← JaMResult.length ← 0;

  FOR i IN [1..targc) DO 
    IF (n ← ufind[targv[i]])=NIL THEN
      { error3String["Cannot set input value"L,"%s*n"L,targv[i]]; LOOP; };
    setin[n,which];
  ENDLOOP;
};
    
setWatchString: PUBLIC PROC =
  {  n: SimTsim.nptr;
  p: STRING;
  w: SimTsim.wptr;
  i: CARDINAL;
  flag:INTEGER;

  JaMError.length ← JaMResult.length ← 0;

  FOR i←1, i+1 WHILE i<targc DO
    p ← targv[i];
    IF p[0] = '- THEN { flag ← 1; StringShiftLeft[p, 1]; } ELSE flag ← 0;
    IF (n ← ufind[p]) = NIL THEN
      { error3String["Cannot watch node"L,"%s*n"L,p]; LOOP; };
    IF (flag)#0 THEN {
      n.watched ← FALSE;
      IF StringDefs.EquivalentString[p,wlist.wname] THEN wlist ← wlist.wnext
      ELSE FOR w ← wlist, w.wnext WHILE w.wnext#NIL DO
        IF StringDefs.EquivalentString[p,w.wnext.wname] THEN { w.wnext ← w.wnext.wnext; EXIT; }
      ENDLOOP
    } ELSE IF n.watched THEN LOOP 
    ELSE {
      n.watched ← TRUE;
      w ← SimStorage.AllocateWnode[];
      w.wnext ← wlist;
      wlist ← w;
      w.wname ← SystemDefs.AllocateHeapString[p.length]; -- Released by main
      StringDefs.AppendString[w.wname,p];
    };
  ENDLOOP;
  FOR w ← wlist, w.wnext WHILE w#NIL DO
    n ← ufind[w.wname]; 
    n.watched ← TRUE;
  ENDLOOP;
};

setBitsString: PUBLIC PROC =
  {  n: SimTsim.nptr;
  b: bptr;
  i: CARDINAL;
  
  JaMError.length ← JaMResult.length ← 0;

  IF targc - 2>=MAXBITS THEN {
    error2String["Too many bits to watch (%d)"L,targc - 2]; 
    RETURN};
  b ← SystemDefs.AllocateHeapNode[SIZE[Bits]]; -- Released by main
  b.bnext ← blist;
  blist ← b;
  b.bnbits ← targc - 2;
  b.bname ← SystemDefs.AllocateHeapString[targv[1].length]; -- Released by main
  StringDefs.AppendString[b.bname, targv[1]];

  FOR i IN [2..targc) DO
    IF (n ← ufind[targv[i]]) = NIL THEN
      { error3String["Cannot watch node"L,"%s*n"L,targv[i]]; LOOP; };
    b.bbits[i - 2] ← n;
  ENDLOOP;
};

writeValue: PROC[n: SimTsim.nptr] RETURNS[CARDINAL] =
  {  putc[LOOPHOLE[n.npot, CARDINAL] + 40C,sfile];
  RETURN[0];
};

writeStateString: PUBLIC PROC =
  {  JaMError.length ← JaMResult.length ← 0;

  IF targc # 2 THEN {
    error2String["No file specified for output"L,"*n"L];
    RETURN;
  };
  IF (sfile ← fopen[targv[1],"w"L]) = NIL THEN {
    error3String["Cannot open state file for output"L,"%s*n"L,targv[1]];
    RETURN;
  };
  FWF1[sfile,"%d*n"L,nindex];
  [] ← walkNet[writeValue];
  extWrite[sfile];
  fclose[sfile];
};
    
readValue: PROC[n: SimTsim.nptr] RETURNS[CARDINAL] =
  {  ch: CHARACTER;
  WHILE (ch ← getch[sfile]) < '  DO ENDLOOP;

  n.npot ← LOOPHOLE[ch - ' , SimTsim.Potential];
  RETURN[0];
};

readStateString: PUBLIC PROC =
  {  rline: STRING ←[100];

  JaMError.length ← JaMResult.length ← 0;

  IF (targc # 2) THEN {
    error2String["No file specified for input"L,"*n"L];
    RETURN;
  };
  IF (sfile ← fopen[targv[1],"r"L]) = NIL THEN {
    SWF1[JaMError,"Cannot open state file for input: %s*n"L,targv[1]];
    RETURN;
  };
  [] ← fgets[rline,100,sfile];
  rline.length ← rline.length - 1;
  IF atoi[rline] # nindex THEN {
    SWF1[JaMError,"State file has wrong number of nodes: %d*n"L,nindex];
    RETURN;
  };
  [] ← walkNet[readValue];
  extRead[sfile];
  fclose[sfile];
};
    
infoResult: PUBLIC PROC[which: CARDINAL] =
  {  n,m: SimTsim.nptr;
  t: SimTsim.tptr;
  p: LONG POINTER TO SimTsim.nptr;
  realForPrint: REAL;
  i: CARDINAL;

  offset:LONG INTEGER ← LOOPHOLE[@n.changecount, LONG INTEGER] - 1 - LOOPHOLE[n, LONG INTEGER];

  JaMError.length ← JaMResult.length ← 0;

  FOR i ← 1, i + 1 WHILE i < targc DO
    IF (n ← ufind[targv[i]]) = NIL THEN
      { error3String["Cannot find node"L,"%s*n"L,targv[i]]; LOOP; };

    pvalue[n];
    IF n.input THEN
      FWF0[outfile,"[NOTE: node is an input] "L];

    realForPrint ← n.ncap;
    IF n.ncap # 0.0 THEN FWF1[outfile,"(capacitance = %f pf) "L,@realForPrint];

    FWF1[outfile,"%s:*n"L,IF which#0 THEN "affects"L ELSE "is computed from"L];
    IF which = 0 THEN {

      IF (p ← LOOPHOLE[n.ndef, LONG POINTER TO SimTsim.nptr])#NIL THEN {
        FWF0[outfile,"   (nor"L];
        WHILE p↑ # NIL DO 
    FWF0[outfile,"*t(and "L];
    DO 
      p ← p+2;
      IF (m ← p↑) = NIL THEN EXIT;
      pvalue[LOOPHOLE[LOOPHOLE[m, LONG INTEGER] - offset, SimTsim.nptr]];
    ENDLOOP;
    FWF0[outfile,")*n"L];
        ENDLOOP;
        FWF0[outfile,"   )*n"L];
      } ELSE IF n.pullup THEN FWF0[outfile,"pulled up*n"L];

      FOR t ← n.nsource, t.slink WHILE t#NIL DO
        IF (t.drain = GNDNode) THEN {
    FWF0[outfile,"pulled down by "L];
    pvalue[t.gate];

    FWF0[outfile,"*n"L];

        } ELSE ptrans[t];
      ENDLOOP;
      FOR t←n.ndrain, t.dlink WHILE t#NIL 
      DO ptrans[t] ENDLOOP;
    } ELSE FOR t←n.ngate, t.glink WHILE t#NIL DO ptrans[t] ENDLOOP;
  ENDLOOP;
};

-- save user name(s) as pseudonym for node(s) 
userNameString: PUBLIC PROC =
  {  n: SimTsim.nptr;
  utemp: SimTsim.uptr;
  i:CARDINAL;
  h:INTEGER;

  JaMError.length ← JaMResult.length ← 0;

  FOR i←1, i+2 WHILE i<targc DO
    IF i+1 = targc THEN { error2String["Bad = syntax"L,"*n"L]; };
    IF (n ← ufind[targv[i]]) = NIL THEN
      { error3String["cannot find node name in ="L,"%s*n"L,targv[i]]; LOOP; };
    IF ufind[targv[i+1]] # NIL THEN
      { error3String["redefinition of user name"L,"%s*n"L,targv[i+1]]; LOOP; };
    utemp ← SimStorage.AllocateUsymbol[];
    h ← hashcode[targv[i+1]];
    utemp.unext ← uhash[h];
    uhash[h] ← utemp;
    utemp.unode ← n;
    utemp.uname ← SystemDefs.AllocateHeapString[targv[i+1].length]; -- Released by main
    StringDefs.AppendString[utemp.uname,targv[i+1]];
  ENDLOOP;
};

-- check that node(s) has specified value(s), return true if all results matched 
checkVal: PROC RETURNS[INTEGER] =
  {  n: SimTsim.nptr;
  b: bptr;
  i, f:CARDINAL;

  FOR i IN [1..targc) DO 
    IF i+1 = targc THEN { error2String["Bad check syntax"L,"*n"L]; RETURN[1]; };
    IF (n ← ufind[targv[i]])#NIL AND targv[i+1][1]=0C THEN {
      IF SimTsim.pchars[n.npot] = targv[i+1][0] THEN LOOP;
      error5String["Check mismatch"L,
      "node %s: found %c, wanted %c*nReverting to tty input...*n"L,
      pnode[n],SimTsim.pchars[n.npot],targv[i+1][0]];
      RETURN[0];
    };

    FOR b←blist, b.bnext WHILE b#NIL DO
      IF StringDefs.EquivalentString[targv[i],b.bname] THEN {
        FOR f IN [0..b.bnbits) DO
    IF SimTsim.pchars[b.bbits[f].npot] = targv[i+1][f] THEN LOOP;
    error6String["Check mismatch"L,
          "vector %s, bit %d: found %c, wanted %c*nReverting to tty input...*n"L,
          b.bname,f,SimTsim.pchars[b.bbits[f].npot],targv[i+1][f]];
    RETURN[0];
        ENDLOOP;
        EXIT;
      };
    ENDLOOP;
  ENDLOOP;
  RETURN[1];
};

resetChangeCount: PUBLIC PROC = {
  index: CARDINAL;
  n: SimTsim.nptr;
  FOR index IN [0..HASHSIZE) DO
    FOR n ← hash[index], n.hnext UNTIL n=NIL DO
            n.changecount ← 0
    ENDLOOP;
  ENDLOOP;
};

Swap: PROC[left, right: CARDINAL, quick: DESCRIPTOR FOR ARRAY OF LONG POINTER] = {
  temp: SimTsim.nptr ← quick[left];
  quick[left] ← quick[right];
  quick[right] ← temp
};

quickSort: PROC [left, right: CARDINAL, quick: DESCRIPTOR FOR ARRAY OF SimTsim.nptr] = {
  comp: CARDINAL ← quick[left].changecount;
  oldLeft: CARDINAL ← left;
  oldRight: CARDINAL ← right;
  -- scan right to left
  DO
    UNTIL left = right DO
      IF quick[right].changecount < comp THEN EXIT;
      right ← right - 1;
    REPEAT
      FINISHED => {
        IF oldLeft + 1 < left THEN quickSort[oldLeft, left - 1, quick];
        IF right + 1 < oldRight THEN quickSort[right + 1, oldRight, quick]; RETURN}; 
    ENDLOOP;
    Swap[left, right, quick];
    left ← left + 1;
    UNTIL left = right DO
      IF quick[left].changecount > comp THEN EXIT;
      left ← left + 1;
    REPEAT
      FINISHED => {
        IF oldLeft + 1 < left THEN quickSort[oldLeft, left - 1, quick];
        IF right + 1 < oldRight THEN quickSort[right + 1, oldRight, quick]; RETURN}; 
    ENDLOOP;
    Swap[left, right, quick];
    right ← right - 1;
  ENDLOOP;
};

sortNodes: PROC[quick: DESCRIPTOR FOR ARRAY OF SimTsim.nptr] = {
  sub, index: CARDINAL;
  n: SimTsim.nptr;
  sub ← 0;
  FOR index IN [0..HASHSIZE) DO
    FOR n ← hash[index], n.hnext UNTIL n=NIL DO
            quick[sub] ← n; sub ← sub + 1
    ENDLOOP;
  ENDLOOP;
  quickSort[0, LENGTH[quick] - 1, quick];
};

PrintNode: PROC[node: SimTsim.nptr] = {
  FWF1[log, "%s"L, pnode[node]]
};

PrintCount: PROC[num: CARDINAL] = {
  FWF1[log, "%u:  "L, num];
};

printNodes: PROC[quick: DESCRIPTOR FOR ARRAY OF SimTsim.nptr] = {
  currentCount, index: CARDINAL;
  currentCount ← quick[0].changecount;
  PrintCount[currentCount];
  PrintNode[quick[0]];
  FOR index IN [1..LENGTH[quick]) DO
    IF quick[index].changecount = currentCount THEN {
      FWF0[log, ","L];
      PrintNode[quick[index]]}
    ELSE {
      FWF0[log, ";*n"L];
      currentCount ← quick[index].changecount;
      PrintCount[currentCount];  
      PrintNode[quick[index]]}
  ENDLOOP;
  FWF0[log, ";*n"L];
};

nodeCount: PROC RETURNS[CARDINAL] = {
  count, index: CARDINAL;
  n: SimTsim.nptr;
  count ← 0;
  FOR index IN [0..HASHSIZE) DO
    FOR n ← hash[index], n.hnext UNTIL n=NIL DO
      count ← count + 1
    ENDLOOP;
  ENDLOOP;
  RETURN[count];
};

displayChangedNodes: PUBLIC PROC = { OPEN StreamDefs;
  date: STRING ← [30];
  IF log = NIL THEN log ← StreamDefs.NewByteStream["MOSSIM.log"L,StreamDefs.Append];
  SetPosition[log, IndexToPosition[FileLength[log]]];
  FWF0[log, "*nStatistics of Nodes that Changed Potential*n"L];
  TimeDefs.AppendDayTime[date, 
    TimeDefs.UnpackDT[TimeDefs.CurrentDayTime[]],];
  FWF1[log, "%s*n*n"L, date];

  BEGIN
    quick: DESCRIPTOR FOR ARRAY OF SimTsim.nptr;
    count: CARDINAL ← nodeCount[];
    quick ← DESCRIPTOR[SystemDefs.AllocateHeapNode[count*2], count];
    sortNodes[quick];
    printNodes[quick];
    SystemDefs.FreeHeapNode[BASE[quick]];
  END;

  log.destroy[log];
  log ← NIL;
};

traceNodes: PUBLIC PROC = {
  i: CARDINAL;
  n: SimTsim.nptr;
  IF log = NIL THEN log ← StreamDefs.NewByteStream["MOSSIM.log"L,StreamDefs.Append];
  FOR i←1, i+1 WHILE i<targc DO
    IF (n ← ufind[targv[i]]) = NIL THEN
      { error3String["Cannot trace node"L,"%s*n"L,targv[i]]; LOOP};
    IF NOT n.traced THEN 
      {n.traced ← TRUE; traceNumber ← traceNumber+1};
  ENDLOOP;
};

unTraceNodes: PUBLIC PROC = {
  i: CARDINAL;
  n: SimTsim.nptr;
  FOR i←1, i+1 WHILE i<targc DO
    IF (n ← ufind[targv[i]]) = NIL THEN
      { error3String["Cannot untrace node"L,"%s*n"L,targv[i]]; LOOP};
    IF n.traced THEN 
      {n.traced ← FALSE; traceNumber ← traceNumber-1};
  ENDLOOP;
};

error2String: PROC[a: STRING, b: UNSPECIFIED] =
  {  SWF3[JaMError,"[%s,%d] %s: "L,filename,lineno,a];
  SWF0[JaMError,b];
  WF3["[%s,%d] %s: "L,filename,lineno,a];
  WF0[b];
};

error3String: PROC[a: STRING,b,c: UNSPECIFIED] =
  {  SWF3[JaMError,"[%s,%d] %s: "L,filename,lineno,a];
  SWF1[JaMError,b,c];
  WF3["[%s,%d] %s: "L,filename,lineno,a];
  WF1[b,c];
};

error4String: PROC[a: STRING,b,c,d: UNSPECIFIED] =
  {  SWF3[JaMError,"[%s,%d] %s: "L,filename,lineno,a];
  SWF2[JaMError,b,c,d];
  WF3["[%s,%d] %s: "L,filename,lineno,a];
  WF2[b,c,d];
};

error5String: PROC[a: STRING,b,c,d,e: UNSPECIFIED] =
  {  SWF3[JaMError,"[%s,%d] %s: "L,filename,lineno,a];
  SWF3[JaMError,b,c,d,e];
  WF3["[%s,%d] %s: "L,filename,lineno,a];
  WF3[b,c,d,e];
};

error6String: PROC[a: STRING,b,c,d,e,f: UNSPECIFIED] =
  {  SWF3[JaMError,"[%s,%d] %s: "L,filename,lineno,a];
  SWF4[JaMError,b,c,d,e,f];
  WF3["[%s,%d] %s: "L,filename,lineno,a];
  WF4[b,c,d,e,f];
};

-- parse input line into tokens, filling up targv and setting targc 
parseLine: PUBLIC PROC[line: STRING] =
  {  pos: CARDINAL ← 0;

  Get: PROC[s: STRING] RETURNS[STRING] = {
    char: CHARACTER;
    end, length: CARDINAL;
    s.length ← length ← 0;
    IF (end ← pos)>=line.length THEN RETURN[NIL];
    DO
      SELECT (char ← line[end]) FROM
        ' , IODefs.TAB, ', => EXIT;
        IODefs.CR => IF end=0 THEN RETURN[NIL] ELSE EXIT;
      ENDCASE => {s[length] ← char; end ← end + 1; length ← length + 1}
    ENDLOOP;
    s.length ← length;
    pos ← end + 1;
    RETURN[s]
  };

  FOR targc ← 0, targc + 1 UNTIL Get[targv[targc]]=NIL DO
  ENDLOOP;
};

MOSSIMEnter: PUBLIC PROC = { 
  input[stdin];
  CleanUp[]};

input: PROC[in: FILE] =
  {  
  i, nsteps: CARDINAL;
  line: STRING ← [LSIZE];

  DO
    IF in = stdin THEN FWF0[stderr,"sim> "L];
    line.length ← 0;
    IF fgets[line,LSIZE,in
      !IODefs.Rubout => {IF in = stdin THEN FWF0[stderr,"*n"L]; LOOP}] # line THEN EXIT;
    lineno ← lineno + 1; -- this is the only place lineno is increased
    parseLine[line];
    IF (targv[0] = NIL) THEN LOOP;
    SELECT targv[0][0] FROM 

      '@ =>  readFileString[];

      'D =>  Debug[];

      '< =>  readStateString[]; 

      '> =>  writeStateString[]; 

      '| =>  NULL;

      'e =>  eTrans[];

      'd =>  dTrans[]; 

      'N =>  nodeInfoString[]; 

      'h, 'l , 'x =>  setInputString[targv[0][0]]; 

      '= =>  userNameString[]; 

      'w =>  setWatchString[]; 

      'W =>  setBitsString[]; 

      'v =>  viewString[];

      'I =>  initialization[];

      'c =>  IF targv[0][1] = 'h AND checkVal[]=0 
      THEN in ← stdin 
      ELSE {
      IF (targc = 1) THEN i ← 1
      ELSE { i ← atoi[targv[1]]; IF (i <= 0) THEN i ← 1; };
      nsteps ← 0;
      WHILE i#0 DO
        i ← i-1;
        setin[PHI1Node,'h];
        setin[PHI2Node,'l];
        nsteps ← nsteps + step[];
        setin[PHI1Node,'l];
        nsteps ← nsteps + step[];
        setin[PHI2Node,'h];
        nsteps ← nsteps + step[];
        setin[PHI2Node,'l];
        nsteps ← nsteps + step[];
      ENDLOOP;
      pnlist[];
      FWF1[outfile,"cycle took %d events*n"L,nsteps];
      };

      's =>  {FWF1[outfile,"step took %d events*n"L,step[]];
      pnlist[]
      };

      'i =>  NULL;

      '? =>  infoResult[0]; 
      '! =>  infoResult[1]; 
      'X =>  SimTsim.extension[targc,@targv]; 

      'q =>  RETURN;

    ENDCASE =>  error3String["Unrecognized input line"L,"%s*n"L,line];
    
  ENDLOOP; -- infinite loop
};

viewString: PROC = {  
  list: POINTER TO ARRAY OF SimTsim.nptr;
  i: INTEGER;

  JaMError.length ← 0;

  pnlist[];
  FWF0[outfile,"h inputs: "L];
  list←LOOPHOLE[@hinputs, POINTER TO ARRAY OF SimTsim.nptr];
  FOR i IN [0..nhinputs) DO
    FWF1[outfile,"%s "L,pnode[list[i]]];
  ENDLOOP;
  FWF0[outfile,"*nl inputs: "L];
  list←LOOPHOLE[@linputs, POINTER TO ARRAY OF SimTsim.nptr];
  FOR i IN [0..nlinputs) DO
    FWF1[outfile,"%s "L,pnode[list[i]]];
  ENDLOOP;
  FWF0[outfile,"*n"L];
  };

readFileString: PUBLIC PROC = {
  next: FILE;
  ofname: STRING;
  olineno: INTEGER;
  AppendOptionalSuffix: PROC[name: STRING] RETURNS [STRING] = {
    i: CARDINAL;
    ret: STRING ← SystemDefs.AllocateHeapString[name.length+4];
    FOR i←0, i+1 UNTIL i>=name.length DO
      c: CHARACTER ← name[i];
      StringDefs.AppendChar[ret, c]; 
      IF c='. THEN GOTO DotFound;
      REPEAT
        DotFound => {
          FOR i←i+1, i+1 UNTIL i>=name.length DO
            StringDefs.AppendChar[ret, name[i]];
            ENDLOOP};
        FINISHED => StringDefs.AppendString[ret, ".sim"L];
      ENDLOOP;
    RETURN[ret]};
  JaMError.length ← JaMResult.length ← 0;

  IF targc # 2 THEN
    error3String["wrong number of args to '@' command"L,"%d*n"L,targc]
  ELSE {
    simfile: STRING ← AppendOptionalSuffix[targv[1]]; -- this is storage-leak free
    IF (next ← fopen[simfile,"r"L]) = NIL THEN
        error3String["cannot open for input"L,"%s*n"L,simfile]
    ELSE {
        ofname ← filename; olineno ← lineno;
        filename ← targv[1]; lineno ← 0;
        input[next];
        fclose[next];
        filename ← ofname; lineno ← olineno};
    SystemDefs.FreeHeapString[simfile]; -- this is checked
  };
  WF.WF0["*n"L]
};

Debug: PUBLIC PROC = { debug ← 1-debug }; 

eTrans: PUBLIC PROC = { newtransString[0] }; 

dTrans: PUBLIC PROC = { newtransString[1] }; 

setInputH: PUBLIC PROC = {setInputString['h]};

setInputL: PUBLIC PROC = {setInputString['l]};

setInputX: PUBLIC PROC = {setInputString['x]};

question: PUBLIC PROC = {infoResult[0]};

exclamation: PUBLIC PROC = {infoResult[1]};

getNodeValue: PUBLIC PROC RETURNS[INTEGER] = {
  n: SimTsim.nptr ← ufind[targv[1]];

  JaMError.length ← JaMResult.length ← 0;

  IF n=NIL THEN error2String["This node is not defined"L, "*n"L]; 
  RETURN[intvalue[n.npot]]};

initialization: PROC = {
  FWF1[outfile,"initialization took %d steps*n"L,einit[]]};

initializationResult: PUBLIC PROC RETURNS[CARDINAL] = {

  JaMError.length ← JaMResult.length ← 0;

  RETURN[einit[]]};

cycleResult: PUBLIC PROC RETURNS[CARDINAL] = {
      i, nsteps: CARDINAL;

  JaMError.length ← JaMResult.length ← 0;

  IF (targc = 1) THEN i ← 1
  ELSE { i ← atoi[targv[1]]; IF (i <= 0) THEN i ← 1; };
  nsteps ← 0;
  WHILE i#0 DO
    i ← i-1;
    setin[PHI1Node,'h];
    setin[PHI2Node,'l];
    nsteps ← nsteps + step[];
    setin[PHI1Node,'l];
    nsteps ← nsteps + step[];
    setin[PHI2Node,'h];
    nsteps ← nsteps + step[];
    setin[PHI2Node,'l];
    nsteps ← nsteps + step[];
  ENDLOOP;
  pnlist[];
  RETURN[nsteps];
  };

StringShiftLeft: PROC[st: STRING, offset: CARDINAL] = {
  i: CARDINAL;
  FOR i IN [offset..st.length) DO
    st[i-offset] ← st[i]
  ENDLOOP;
  st.length ← st.length - offset };

-- MAIN PROGRAM

main: PROC = {  
  i: INTEGER;

  START StdioImpl;
  START SimTTable;
  START WFRealImpl;

  outfile ← stdout;

-- #ifdef TIMING
--  trace ← fopen["trace.out"L,"w"L];
-- #endif

  FOR i ← 0, i+1 UNTIL i>=HASHSIZE DO
          uhash[i] ← NIL;
          hash[i] ← NIL;
  ENDLOOP;

-- Allocates JaM communication: These storage are released by main
  JaMResult ← SystemDefs.AllocateHeapString[400];
  JaMError ← SystemDefs.AllocateHeapString[200];

-- Allocates targv
  FOR i IN [0..SimTsim.MaxTargv) DO
    targv[i] ← SystemDefs.AllocateHeapString[50];
  ENDLOOP;

-- Allocates predefined nodes
  VDDNode ← getnode["VDD"L];
  setin[VDDNode,'h];
  VDDNode.npot ← DHIGH;

  GNDNode ← getnode["GND"L];
  setin[GNDNode,'l];
  GNDNode.npot ← DLOW;

  PHI1Node ← getnode["phi1"L];
  PHI2Node ← getnode["phi2"L];

-- Read from the command line 
IF NOT MOSSIMFns.UnderJaM THEN {
  argc, argno: CARDINAL;
  argv: POINTER TO ARRAY [0..10) OF STRING;
  argarray: ARRAY [0..10) OF STRING;
  in: FILE;
  argc ← 1;
  argv ← @argarray;
  ComParse.Open[,,];
  UNTIL (argv[argc] ← ComParse.Get[ ])=NIL DO argc ← argc+1 ENDLOOP;
  ComParse.Close[];
  FOR argno ← 1, argno + 1 WHILE argno < argc DO
    filename ← argv[argno];
    IF filename[0] = '- THEN {
      IF outfile # stdout THEN fclose[outfile];
      IF filename.length=1 THEN { outfile ← stdout; LOOP; };
      StringShiftLeft[filename, 1];
      outfile ← fopen[filename,"w"L];
      IF outfile = NIL THEN {
        FWF1[stderr,"Cannot open output file: %s*n"L,filename];
        outfile ← stdout;
      };
      LOOP;
    };
    lineno ← 0;
    in ← fopen[filename,"r"L];
    IF in = NIL THEN {
      FWF1[stderr,"Cannot open input file: %s*n"L,filename];
      LOOP;
    } ELSE input[in];
    fclose[in];
  ENDLOOP;
  FWF3[outfile,"%d transistors, %d nodes (%d pulled up)*n"L, tindex, nindex, nion];
  IF outfile # stdout THEN fclose[outfile];
  outfile ← stdout;
  
};

filename ← "tty";
lineno ← 0
}; -- end main

CleanUp: PROC = {
-- Frees all the storage

-- Frees JaM communcation area
SystemDefs.FreeHeapString[JaMResult];
SystemDefs.FreeHeapString[JaMError];

-- Frees targv
{i: CARDINAL;
   FOR i IN [0..SimTsim.MaxTargv) DO
    SystemDefs.FreeHeapString[targv[i]];
   ENDLOOP};

-- Frees the data structures accessible from hash table
{ hnext: SimTsim.nptr; i: CARDINAL;
   FOR i IN [0..HASHSIZE) DO
    IF (hnext ← hash[i])#NIL THEN {
      hash[i] ← NIL;
      DO
        IF hnext.named THEN SystemDefs.FreeHeapString[hnext.nname];
        hnext ← hnext.hnext;
        IF hnext = NIL THEN EXIT;
      ENDLOOP}
    ENDLOOP
};
{ wnext: SimTsim.wptr;
   FOR wnext ← wlist, wnext.wnext UNTIL wnext=NIL DO
    SystemDefs.FreeHeapString[wnext.wname];
    ENDLOOP};
{ before: bptr;
   DO
    IF blist = NIL THEN EXIT;
    before ← blist;
    SystemDefs.FreeHeapString[blist.bname];
    blist ← blist.bnext;
    SystemDefs.FreeHeapNode[before];
    ENDLOOP};
{ unext: SimTsim.uptr; i: CARDINAL;
   FOR i IN [0..HASHSIZE) DO
    IF (unext ← uhash[i])#NIL THEN {
      uhash[i] ← NIL;
      DO
        SystemDefs.FreeHeapString[unext.uname];
        unext.uname ← NIL;
        unext ← unext.unext;
        IF unext = NIL THEN EXIT;
      ENDLOOP}
    ENDLOOP};

-- Release all the nodes
  SimStorage.FreeNodes[];
}; -- ClenaUp

-- Main program

main[];

}.  -- MOSSIM