/****************************************************************************
    Copyright (C) 1987-2005 by Jeffery P. Hansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    Last edit by hansen on Fri May  7 12:56:25 2004
****************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <ctype.h>
#include "tkgate.h"

#define MAXDEPTH	128

extern int debugSimInterface;

extern GScope *Scope;

static void doScriptCommand(ClientData data);

/*
 * Parse a verilog-style constant as received from the simulator.
 * Only 'b' and 'h' types are used, with 'b' types being used only
 * for single-bit values.
 */ 
void parse_verilog_constant(char *value,unsigned *ivalue,unsigned *ivalid,int *n)
{
  char c,*p;

  *n = 0;
  *ivalue = 0;
  *ivalid = 0;

  if (sscanf(value,"%d'%c",n,&c) != 2) return;
  if (c == 'b' && *n != 1) return;
  if (c != 'h' && c != 'b') return;
  if (*n > 32) return;

  p = strchr(value,'\'');
  p += 2;
  while (*p) {
    *ivalue <<= 4;
    *ivalid <<= 4;

    if (*p >= '0' && *p <= '9') {
      *ivalue |= (*p - '0') & 0xf;
      *ivalid |= 0xf;
    } else if (*p >= 'a' && *p <= 'f') {
      *ivalue |= (*p - 'a' + 10) & 0xf;
      *ivalid |= 0xf;
    } else if (*p >= 'A' && *p <= 'F') {
      *ivalue |= (*p - 'A' + 10) & 0xf;
      *ivalid |= 0xf;
    } else if (*p == '(') {
      int i;

      for (i = 0;i < 4;i++) {
	if (*p == '1') {
	  *ivalue |= 1 << i;
	  *ivalid |= 1 << i;
	} else if (*p == '0') {
	  *ivalid |= 1 << i;
	}
	p++;
      }
      p++;
    } else {
      *ivalid |= 0;
    }
    p++;
  }
}


static void pickProbePosition(GWireNode *n,int *x,int *y)
{
  GWireNode *in = n->in;
  GWireNode *on = n->out;
  GWireNode *xn;

  if (!in)
    xn = on;
  else if (!on)
    xn = in;
  else {
    int id, od;
    id = od = 0x7ffffff;

    if (n->y == in->y)
      id = iabs(*y - n->y);
    else
      id = iabs(*x - n->x);

    if (n->y == on->y)
      od = iabs(*y - n->y);
    else
      od = iabs(*x - n->x);
    xn = (id < od) ? in : on;
  }

  if (!xn) return;

  if (xn->x == n->x) {
    int miny = imin(xn->y,n->y);
    int maxy = imax(xn->y,n->y);
    *x = n->x;
    if (*y < miny) *y = miny;
    if (*y > maxy) *y = maxy;
  } else {
    int minx = imin(xn->x,n->x);
    int maxx = imax(xn->x,n->x);
    *y = n->y;
    if (*x < minx) *x = minx;
    if (*x > maxx) *x = maxx;
  }
}

void sendSimCmd(char *fmt,...)
{
  char buf[STRMAX];
  va_list ap;

  va_start(ap,fmt);
  vsprintf(buf,fmt,ap);
  va_end(ap);

  DoTcl("tkg_simWrite {%s}",buf);
  
}

GSimSwitch *new_GSimSwitch(char *wname,char *gname,GCElement *g)
{
  GSimSwitch *ss = (GSimSwitch*)ob_malloc(sizeof(GSimSwitch),"GSimSwitch");

  ss->name = ob_strdup(wname);
  ss->gname = ob_strdup(gname);
  ss->gate = g;
  ss->state = (unsigned*)ob_malloc(sizeof(unsigned),"unsigned");
  *ss->state = g->u.sw.dipval;

  return ss;
}

GSimLed *new_GSimLed(char *wname,char *gname,GCElement *g)
{
  GSimLed *ss = (GSimLed*)ob_malloc(sizeof(GSimLed),"GSimLed");

  ss->name = ob_strdup(wname);
  ss->gname = ob_strdup(gname);
  ss->gate = g;

  return ss;
}

GSimModule *new_GSimModule(GModuleDef *M,GCElement *g,GSimModule *P)
{
  GSimModule *sM = (GSimModule*) ob_malloc(sizeof(GSimModule),"GSimModule");

  sM->mod = M;
  sM->inst = g;
  sM->parent = P;

  sM->probes = new_SHash();
  sM->switches = new_SHash();
  sM->leds = new_SHash();
  sM->children = new_SHash();

  return sM;
}

GNet *sim_findNet(const char *name)
{
  char buf[STRMAX],*T;
  char *ptr[MAXDEPTH];
  GModuleDef *M = XGate.circuit->root_mod;
  GNet *n = 0;
  int N,i;

  strcpy(buf,name);
  for (T = strtok(buf,"."), N = 0;T;T = strtok(0,"."), N++)
    ptr[N] = T;

  for (i = 0;i < N;i++) {
    if (i != (N-1)) {
      GCElement *g = GModuleDef_findGate(M,ptr[i]);
      if (g && g->typeinfo->Code == BLOCK)
	M = env_findModule(g->u.block.BlockFunction);
      else
	return 0;
    } else
      n = GModuleDef_findNet(M,ptr[i]); 
  }

  return n;
}

GCElement *sim_findGate(const char *name)
{
  char buf[STRMAX],*T;
  char *ptr[MAXDEPTH];
  GModuleDef *M = XGate.circuit->root_mod;
  int N,i;
  GCElement *g = 0;

  strcpy(buf,name);
  for (T = strtok(buf,"."), N = 0;T;T = strtok(0,"."), N++)
    ptr[N] = T;

  for (i = 0;i < N;i++) {
    g = GModuleDef_findGate(M,ptr[i]);
    if (i == (N-1))
      break;
    else if (g && g->typeinfo->Code == BLOCK)
      M = env_findModule(g->u.block.BlockFunction);
    else
      return 0;
  }
  return g;
}

GModuleDef *sim_findContainingMod(const char *path)
{
  char buf[STRMAX],*p;
  GModuleDef *M;

  strcpy(buf,path);
  if ((p = strrchr(buf,'.'))) {
    GCElement *mg;
    *p = 0;
    mg = sim_findGate(buf);
    if (mg)
      M = env_findModule(mg->u.block.BlockFunction);
    else
      M = 0;
  } else
    M = XGate.circuit->root_mod;

  return M;
}

void getSimTempFile(char *buf)
{
  int fd;

  strcpy(buf,"/tmp/tkgate.XXXXXX");
  fd = mkstemp(buf);
  close(fd);
}

/*
  Checks the syntax of a breakpoint.  Generate an error dialog if there
  is an error.
 */
int breakpoint_check(const char *s)
{
  char buf1[STRMAX],buf2[STRMAX],op[16],x,*p;
  int nargs = 0;
  int bad = 0;

  if (sscanf(s," %[^ \t=!] == %[^ \t] %c",buf1,buf2,&x) == 2) {
    strcpy(op,"==");
    nargs = 2;
  } else if (sscanf(s," %[^ \t=!] != %[^ \t] %c",buf1,buf2,&x) == 2) {
    strcpy(op,"!=");
    nargs = 2;
  } else if (sscanf(s," %[^ \t=!] %c",buf1,&x) == 1) {
    strcpy(op,"");
    nargs = 1;
  } else if (sscanf(s," ! %[^ \t=!] %c",buf1,&x) == 1) {
    strcpy(op,"!");
    nargs = 1;
  } else {
    /* Syntax error in breakpoint expression.  Must be one of: 'net', '!net', 'net==value', 'net!=value' */
    message(1,msgLookup("err.bkpt.badexp"));
    return -1;
  }

  if (!isalpha((int)*buf1) && *buf1 != '_') {
    bad = 1;
  } else {
    for (p = buf1+1;*p;p++)
      if (!isalnum((int)*p) && *p != '.' && *p != '_') {
	bad = 1;
	break;
      }
  }
  if (bad) {
    /* Invalid net name '%s'. Names must start with a letter and contain only letters, digits and '.'. */
    message(1,msgLookup("err.bkpt.badnet"),buf1);
    return -1;
  }

  if (nargs == 1) {
    return 0;
  } else {
    int n;
    char buf3[STRMAX];

    if (sscanf(buf2,"%d'%c%s",&n,&x,buf3) == 3) {
      switch (x) {
      case 'd' :
	for (p = buf3;*p;p++)
	  if (!isdigit((int)*p)) {
	    bad = 1;
	    break;
	  }
	break;
      case 'o' :
	for (p = buf3;*p;p++)
	  if (strchr("01234567xXzZ",*p) == 0) {
	    bad = 1;
	    break;
	  }
	break;
      case 'h' :
	for (p = buf3;*p;p++)
	  if (strchr("0123456789aAbBcCdDeEfFxXzZ",*p) == 0) {
	    bad = 1;
	    break;
	  }
	break;
      case 'b' :
	for (p = buf3;*p;p++)
	  if (strchr("01xXzZ",*p) == 0) {
	    bad = 1;
	    break;
	  }
	break;
      default :
	bad = 1;
	break;
      }
    } else if (sscanf(buf2,"%d %c",&n,&x) == 1) {
    } else
      bad = 1;

    if (bad) {
      message(1,msgLookup("err.bkpt.badval"),buf2);
      return -1;
    }

    return 0;
  }
} 

void SimInterface_init(SimInterface *si)
{
  si->active = 0;
  *si->simFileName = 0;
  si->sim_root = 0;
  si->numBPs = 0;
  si->bp_count = 1;
  si->script = 0;
  si->area = 0;
  si->staticPower = 0;
}

static char *get_path_aux(GSimModule *M,char *s)
{
  if (!M->parent) {
    *s = 0;
    return s;
  }

  s = get_path_aux(M->parent,s);
  return s + sprintf(s,"%s.",M->inst->ename);
}

void GSimModule_getNetPathName(GSimModule *M,GNet *n,char *buf)
{
  char *s;

  s = get_path_aux(M,buf);
  strcpy(s,n->signame);
}


/*
  The the full path of a gate g in the current module.
 */
void GSimModule_getFullPath(GSimModule *M,GCElement *g,char *buf)
{
  char *s;

  s = get_path_aux(M,buf);
  if (g)
    strcpy(s,g->ename);
}


/*
  Lookup the gate named 'name' where 'name' is a full path gate name.  Returns the
  simulation module the gate is in, the GCElement structure and the correspoinding
  GSimSwitch for the specified gate.  Only non-NULL return arguments will be assigned.
  The function returns non-zero if the specified gate is not found.
 */
int SimInterface_lookupGate(SimInterface *si,const char *name,GSimModule **r_M,GCElement **r_g,GSimSwitch **r_ss)
{
  char buf[STRMAX],*T;
  char *ptr[MAXDEPTH];
  GSimModule *M;
  GCElement *g;
  GSimSwitch *ss;
  int i,N;

  M = si->sim_root;

  strcpy(buf,name);
  for (T = strtok(buf,"."), N = 0;T;T = strtok(0,"."), N++)
    ptr[N] = T;

  /*
   * Find parent GSimModule of specified gate.
   */
  for (i = 0;i < N-1;i++) {
    M = (GSimModule*)SHash_find(M->children,ptr[i]);
    if (!M) return -1;
  }

  /*
   * Find the actual gate.
   */
  g = GModuleDef_findGate(M->mod,ptr[N-1]);
  if (!g) {
    return -1;
  }

  /*
   * Find the GSimSwitch if available.
   */
  ss = (GSimSwitch*) SHash_find(M->switches,g->ename);

  if (r_M) *r_M = M;
  if (r_g) *r_g = g;
  if (r_ss) *r_ss = ss;

  return 0;
}

/*
  Lookup the net named 'name' where 'name' is a full path net name.  Returns the
  simulation module the net is in, and the wire.
 */
int SimInterface_lookupWire(SimInterface *si,const char *name,GSimModule **r_M,GWire **r_w)
{
  char buf[STRMAX],*T;
  char *ptr[MAXDEPTH];
  GSimModule *M;
  GNet *n;
  int i,N;

  M = si->sim_root;

  strcpy(buf,name);
  for (T = strtok(buf,"."), N = 0;T;T = strtok(0,"."), N++)
    ptr[N] = T;

  /*
   * Find parent GSimModule of specified wire.
   */
  for (i = 0;i < N-1;i++) {
    M = (GSimModule*)SHash_find(M->children,ptr[i]);
    if (!M) return -1;
  }

  /*
   * Find the actual net.
   */
  n = GModuleDef_findNet(M->mod,ptr[N-1]);
  if (!n) return -1;

  *r_M = M;
  *r_w = n->driver;

  return 0;
}


/*
  Insert breakpoint at a position.  idx=-1 means "at end"
 */
int SimInterface_insertBreakPoint(SimInterface *si,int idx,const char *bp)
{
  if (breakpoint_check(bp))
    return -1;

  if (si->numBPs+1 >= MAXBP) {
    message(1,msgLookup("err.bkpt.toomany"),MAXBP);
    return -1;
  }

  if (idx == -1) {
    idx = si->numBPs++;
  } else {
    int i;

    for (i = si->numBPs;i > idx;i--)
      si->breakpoints[i] = si->breakpoints[i-1];
  }

  si->breakpoints[idx].text = ob_strdup(bp);
  si->breakpoints[idx].id = si->bp_count++;
  si->breakpoints[idx].state = 0;

  if (si->active)
    sendSimCmd("break %d %s",si->breakpoints[idx].id,bp);

  return si->breakpoints[idx].id;
}

void SimInterface_changeCurrentModule(GSimModule *new_sm,GSimModule *old_sm)
{
  char buf[STRMAX];
  HashElem *E;

  if (old_sm) {
    for (E = Hash_first(old_sm->leds);E;E = Hash_next(old_sm->leds,E)) {
      GSimLed *sl = (GSimLed*) HashElem_obj(E);
      GSimModule_getFullPath(old_sm,sl->gate,buf);
      sendSimCmd("ledhide %s",buf);
    }
  }

  if (new_sm) {
    for (E = Hash_first(new_sm->switches);E;E = Hash_next(new_sm->switches,E)) {
      GSimSwitch *ss = (GSimSwitch*) HashElem_obj(E);
      ss->gate->u.sw.dipval = *ss->state;
    }
    for (E = Hash_first(new_sm->leds);E;E = Hash_next(new_sm->leds,E)) {
      GSimLed *sl = (GSimLed*) HashElem_obj(E);
      GSimModule_getFullPath(new_sm,sl->gate,buf);
      sendSimCmd("ledmon %s",buf);
    }
  }
}

/*
  Delete breakpoint at an index (not id number)
 */
void SimInterface_deleteBreakPoint(SimInterface *si,int idx)
{
  int i;

  if (idx >= si->numBPs)
    return;

  if (si->active)
    sendSimCmd("delete_break %d",si->breakpoints[idx].id);

  ob_free(si->breakpoints[idx].text);

  si->numBPs--;
  for (i = idx;i < si->numBPs;i++)
    si->breakpoints[i] = si->breakpoints[i+1];
}

/*
  Send all breakpoints to simulator.
 */
void SimInterface_sendBreakPoints(SimInterface *si)
{
  int i;

  if (si->active) {
    for (i = 0;i < si->numBPs;i++) {
      sendSimCmd("break %d %s",si->breakpoints[i].id,si->breakpoints[i].text);
    }
  }
}

/*
  Show a breakpoint as having been activated.
 */
void SimInterface_seeBreak(SimInterface *si,int id)
{
  int i;

  for (i = 0;i < si->numBPs;i++)
    if (si->breakpoints[i].id == id)
      si->breakpoints[i].state = 1;
}

/*
  Show a breakpoint as having been activated.
 */
void SimInterface_reportBreaks(SimInterface *si)
{
  int i;
  char buf[STRMAX],*p;

  p = buf;
  *p = 0;
  for (i = 0;i < si->numBPs;i++)
    if (si->breakpoints[i].state)
      p += sprintf(p," %d",i);

  DoTcl("tkg_seeBreaks %s",buf);
}

/*
  Clear the 'active' mark on all breakpoints.
 */
void SimInterface_clearBreaks(SimInterface *si)
{
  int i;

  for (i = 0;i < si->numBPs;i++)
    si->breakpoints[i].state = 0;
}

void SimInterface_flushBreaks(SimInterface *si)
{
  int i;

  for (i = 0;i < si->numBPs;i++)
    ob_free(si->breakpoints[i].text);

  si->numBPs = 0;

  DoTcl("catch { destroy .bpedit; destroy .ebp }");
}


static GSimModule *buildMods(GModuleDef *M,GCElement *pg,GSimModule *P)
{
  GSimModule *sM = new_GSimModule(M,pg,P);
  HashElem *E;

  for (E = Hash_first(M->gates);E;E = Hash_next(M->gates,E)) {
    GCElement *g = (GCElement*) HashElem_obj(E);
    GSimModule *csM;
    GModuleDef *cM;
    char buf[STRMAX],buf2[STRMAX];

    switch (g->typeinfo->Code) {
    case BLOCK :
      cM = env_findModule(g->u.block.BlockFunction);
      if (cM) {
	csM = buildMods(cM,g,sM);
	SHash_insert(sM->children,g->ename,csM);
      }
      break;
    case SWITCH :
    case DIP :
      GSimModule_getNetPathName(sM,g->wires[0]->net,buf);
      GSimModule_getFullPath(sM,g,buf2);
      SHash_insert(sM->switches,g->ename,new_GSimSwitch(buf,buf2,g));
      break;
    case LED :
      GSimModule_getNetPathName(sM,g->wires[0]->net,buf);
      GSimModule_getFullPath(sM,g,buf2);
      SHash_insert(sM->leds,g->ename,new_GSimSwitch(buf,buf2,g));
      break;
    }
  }

  return sM;
}

static void SimInterface_buildSimMods(SimInterface *si)
{
  si->sim_root = buildMods(XGate.circuit->root_mod,0,0);
}

GSimProbe *new_GSimProbe(const char *name,GNet *net,int x,int y,GSimModule *sM)
{
  GSimProbe *P = (GSimProbe *) ob_malloc(sizeof(GSimProbe),"GSimProbe");

  P->name = ob_strdup(name);
  P->net = net;
  P->x = x;
  P->y = y;
  P->ss = sM;

  return P;
}

void delete_GSimProbe(GSimProbe *P)
{
  ob_free(P->name);
  ob_free(P);
}

void GSimProbe_draw(GSimProbe *P)
{
  static int probe_icon = -1;

  if (probe_icon < 0) {
    int w,h,x,y;
    Pixmap pm = Pixmap_registerFromFileWithParms("probe","probe.b",&w,&h,&x,&y);
    probe_icon = Icon_register(pm,0,0,w,h,x,y);
  }
  Icon_draw(XGate.D,XGate.W,XGate.toolGC,ctow_x(P->x),ctow_y(P->y),probe_icon);
  dce_DrawString(XGate.toolGC,P->x+20,P->y-20,BetweenTopAndBottom|AtLeft,P->name);
}

void SimInterface_drawProbes(SimInterface *si)
{
  GSimModule *sM = XGate.circuit->es->smod;
  HashElem *E;

  for (E = Hash_first(sM->probes);E;E = Hash_next(sM->probes,E)) {
    GSimProbe *P = (GSimProbe*) HashElem_obj(E);
    GSimProbe_draw(P);
  }
}

int SimInterface_probeExists(SimInterface *si,GSimModule *sM,const char *name)
{
  return SHash_find(sM->probes,name) != 0;
}

void SimInterface_addDelProbe(SimInterface *si,GSimModule *sM,const char *name,GWire *w,GWireNode *n,int x,int y)
{
  GSimProbe *P;
  int visible = (sM == XGate.circuit->es->smod);

  if ((P = SHash_find(sM->probes,name))) {	/* If probe exists, delete it */
    if (visible)
      GSimProbe_draw(P);
    SHash_remove(sM->probes,name);
    delete_GSimProbe(P);
    GScope_deleteTrace(Scope,name);
    sendSimCmd("watch %s 0",name);
  } else {					/* else add it */
    GScope_addTrace(Scope,name,name,w->net->nbits);
    pickProbePosition(n,&x,&y);
    P = new_GSimProbe(name,w->net,x,y,sM);
    SHash_insert(sM->probes,name,P);
    if (visible)
      GSimProbe_draw(P);
    sendSimCmd("watch %s 1",name);
  }
}

void SimInterface_setLed(SimInterface *si,char *gname,char *value)
{
  GCElement *g;
  unsigned ivalue,ivalid;
  int n;

  g = sim_findGate(gname);
  if (!g) return;

  parse_verilog_constant(value,&ivalue,&ivalid,&n);

  if (g->u.led.value != ivalue || g->u.led.valid != ivalid) {
    gate_draw(g,GD_NOWIRE);
    g->u.led.value = ivalue;
    g->u.led.valid = ivalid;
    gate_draw(g,GD_NOWIRE);
  }
}


void SimInterface_hit(SimInterface *si,int x,int y,int isDoubleClick)
{
  GCElement *g = 0;
  GWireNode *n = 0;
  EditState *es = XGate.circuit->es;

  if ((g = gate_hit(XGate.circuit->es->env,x,y))) {
    if (g->typeinfo->Code == JOINT) {
      int i;

      for (i = 0;i < 4;i++)
	if (g->wires[i]) {
	  n = g->wires[i]->nodes;
	  break;
	}
    } else {
      (*g->typeinfo->SimHitFunc)(es,g);
      return;
    }
  } else
    n = wire_iohit(x,y,XGate.circuit->es->env->wires);

  if (n) {
    char buf[STRMAX];
    GWire *w = wirenode_driver(n);

    editstate_getPath(XGate.circuit->es,buf);
    if (*buf) {
      char *p = buf+strlen(buf);
      sprintf(p,".%s",w->net->signame);
    } else
      strcpy(buf,w->net->signame);

    net_select(w->net,1);

    if (isDoubleClick) {
      DoTcl("tkg_simHideValue");
      SimInterface_addDelProbe(si,XGate.circuit->es->smod,buf,w,n,x,y);
    } else {
      DoTcl("tkg_simShowValue %s",buf);
    }
  }
}

void SimInterface_hitRelease(SimInterface *si)
{
  DoTcl("tkg_simHideValue");
}

void SimInterface_send(SimInterface *si,const char *command,...)
{
}

void SimInterface_wireError(SimInterface *si,const char *wname,const char *msg)
{
  GNet *n = sim_findNet(wname);

  if (n)
    Error_nodeReport(wname,n->driver->nodes,msg);
  else
    Error_genericReport(wname,msg);
}

void SimInterface_gateError(SimInterface *si,const char *gname,const char *msg)
{
  GCElement *g = sim_findGate(gname);

  if (g)
    Error_gateReport(gname,g,msg);
  else
    Error_genericReport(gname,msg);
}

void SimInterface_fileError(SimInterface *si,const char *msg)
{
  Error_genericReport("--",msg);
}

void GSimModule_initSwitches(GSimModule *M)
{
  HashElem *E;

  for (E = Hash_first(M->switches);E;E = Hash_next(M->switches,E)) {
    GSimSwitch *ss = (GSimSwitch*) HashElem_obj(E);
    int nbits = ss->gate->wires[0]->net->nbits;
    DoTcl("tkg_simNetSet %s %d'h%x",ss->name,nbits,*ss->state);
  }

  for (E = Hash_first(M->children);E;E = Hash_next(M->children,E)) {
    GSimModule *cM = (GSimModule*) HashElem_obj(E);
    GSimModule_initSwitches(cM);
  }
}

void SimInterface_initSwitches(SimInterface *si)
{
  GSimModule_initSwitches(si->sim_root);
}


/*
 * Called after simulator has loaded circuit and reported that it is ready
 * to start.
 */
void SimInterface_startUp(SimInterface *si)
{
  int i;

  if (si->no_scope) {
    DoTcl("tkg_altStartup");
    return;
  }

  DoTcl("tkg_makeScope");
  SimInterface_initSwitches(si);
  unlink(si->simFileName);
  *si->simFileName = 0;
  SimInterface_sendBreakPoints(si);

  SimInterface_changeCurrentModule(XGate.circuit->es->smod,0);

  DoTcl("tkg_readSimInitScript");

  Circuit_repostProbes(XGate.circuit);

  for (i = 0;i < XGate.circuit->numInitScripts;i++)
    DoTcl("gat_simScript \"%s\"",XGate.circuit->initScripts[i]);
}

/*
 * Commands received from the simulator are processed here.
 */
int SimInterface_command(SimInterface *si,const char *C)
{
  char buf[STRMAX],buf2[STRMAX],buf3[STRMAX];
  int t,a1,a2;
#if 0
  int has_errors = 0;					/* Set to 1 if there were errors in the circuit */
#endif

  if (strncmp(C,"stop",4) == 0) {
    *buf = 0;
  }
  if (debugSimInterface) {
    printf("Scope: %s\n",C);
    fflush(stdout);
  }

  while (*C == ' ')C++;

  if (strncmp(C,"comment",7) == 0) {			/* Ignore comments from simulator */
    return 0;
  } else if (strncmp(C,"echo",4) == 0) {
    printf("gsim: %s\n",C);
    return 0;
  } else if (strncmp(C,"ok",2) == 0) {			/* Simulator loaded file and is ready to go */
    SimInterface_startUp(si);
  } else if (strncmp(C,"error_exit",10) == 0) {		/* The simulator exited on an error */
#if 0
    if (!has_errors) {
      tkgate_setMajorMode(MM_EDIT);
      has_errors = 1;
    }
#endif
    Error_close();
    tkgate_setMajorMode(MM_EDIT);
  } else if (sscanf(C," net %s %s @ %d",buf,buf2,&t) == 3) {	/* The value of a net has changed */ 
    if (GScope_findTrace(Scope,buf)) {
      Scope_stepTo(t);
      Scope_setValue(buf,buf2);
    }
  } else if (sscanf(C," netdelay %s %d %d",buf,&a1,&a2) == 3) {	/* Net delay values */
    cpath_registerNetDelay(buf,a1,a2);
  } else if (sscanf(C," cpath %d %[^\n]",&t,buf) == 2) {	/* Critical path */
    DoTcl("tkg_cpathAdd %d {%s}",t,buf);
  } else if (strncmp(C,"cdone",5) == 0) {			/* End of critical path data */
    DoTcl("tkg_cpathEnd");
  } else if (sscanf(C," stats area=%d static_power=%d",&si->area,&si->staticPower) == 2) {/* Circuit statistics */ 
    message(0,"Estimated area=%d.",si->area,si->staticPower);
  } else if (sscanf(C," valueof %s %s",buf,buf2) == 2) {	/* The value of a net has been requested */ 
    sprintf(buf3,"%s=%s",buf,buf2);
    Tcl_SetVar(XGate.tcl,"tkg_simDisplayedVal",buf3,TCL_GLOBAL_ONLY);
  } else if (sscanf(C," break %d",&t) == 1) {			/* Simulator hit a breakpoint */ 
    DoTcl("tkg_pauseLogo");
    SimInterface_seeBreak(si,t);
  } else if (sscanf(C," stop @ %d",&t) == 1) {			/* Simulator is in pause mode */
    if (si->no_scope) return 0;
    Scope_stepTo(t);
    SimInterface_reportBreaks(si);
    if (si->script && si->script->stop_wait) {
      si->script->stop_wait = 0;
      Tk_DoWhenIdle(doScriptCommand,si->script);
    }
  } else if (sscanf(C," mktty %s",buf) == 1) {			/* Create a tty window */
    DoTcl("tkg_mktty %s",buf);
  } else if (sscanf(C," ledvalue %s %s",buf,buf2) == 2) {	/* Set value of an led */
    SimInterface_setLed(si,buf,buf2);
  } else if (sscanf(C," tty_char %s %d",buf,&t) == 2) {		/* Insert a character */
    DoTcl("tkg_ttyChar %s %d",buf,t);
  } else if (sscanf(C," error net %s %[^\n]",buf,buf2) == 2) {	/* An error on a net */
#if 0
    if (!has_errors) {
      tkgate_setMajorMode(MM_EDIT);
      ob_begin_framef("StartErrs",FF_TRANSPARENT);
      has_errors = 1;
    }
#endif
    SimInterface_wireError(si,buf,buf2);
  } else if (sscanf(C," error gate %s %[^\n]",buf,buf2) == 2) {	/* An error on a gate */
#if 0
    if (!has_errors) {
      tkgate_setMajorMode(MM_EDIT);
      ob_begin_framef("StartErrs",FF_TRANSPARENT);
      has_errors = 1;
    }
#endif
    SimInterface_gateError(si,buf,buf2);
  } else if (sscanf(C," error file %[^\n]",buf) == 1) {		/* An error in a simulator input file. */
#if 0
    if (!has_errors) {
      tkgate_setMajorMode(MM_EDIT);
      ob_begin_framef("StartErrs",FF_TRANSPARENT);
      has_errors = 1;
    }
#endif
    SimInterface_fileError(si,buf);
  } else if (sscanf(C," simerror %[^\n]",buf) == 1) {		/* A run-time error */
    message(1,buf);
    DoTcl("tkg_simStop");
  }

#if 0
  if (has_errors)
    ob_end_frame();
#endif

  return 0;
}

void SimInterface_setSimMods(SimInterface *si)
{
  List L;
  EditState *es;
  ListElem *E;

  List_init(&L);

  for (es = XGate.circuit->es;es;es = es->parent)
    List_addToHead(&L,es);

  E = List_first(&L);
  es = (EditState*) ListElem_obj(E);
  es->smod = si->sim_root;
  for (E = List_next(&L,E);E;E = List_next(&L,E)) {
    es = (EditState*) ListElem_obj(E);
    assert(es->inst);
    /*    printf("smod = %s\n",es->inst->ename);*/
    es->smod = (GSimModule*)SHash_find(es->parent->smod->children,es->inst->ename);
  }

  List_uninit(&L);
}

void SimInterface_begin(SimInterface *si)
{
  EditState *es;

  editstate_makeRootAtTop(&XGate.circuit->es);
  es = XGate.circuit->es;

  getSimTempFile(si->simFileName);		/* Get a temp file for circuit */

  if (VerilogWriteModules(si->simFileName,1) != 0) {		/* Save circuit to send to simulator */
    /* Could not save temporary file '%s' for simulator (disc full?) */
    message(1,msgLookup("err.sim.badtmp"),si->simFileName);
    return;
  }

  SimInterface_buildSimMods(si);		/* Build the simulator hierarchy */

  /*
     Check to see if the top module on the module stack is really the root
     circuit.  If not, switch to the root circuit.
   */
  for (es = XGate.circuit->es;es->parent;es = es->parent);
  if (XGate.circuit->root_mod != es->env) {
    while (XGate.circuit->es)
      editstate_pop(&XGate.circuit->es);
    editstate_push(&XGate.circuit->es,XGate.circuit->root_mod,0);
    editstate_setCurrent(XGate.circuit->es);
    FlagRedraw();

    /* Simulation mode requires root module at top of edit stack. */
    message(0,msgLookup("msg.sim.chgtoroot"));
  }

  /*
     Pop modules that don't have a concrete gate associated with them.
   */
  if (XGate.circuit->es->parent && !XGate.circuit->es->inst) {
    while (XGate.circuit->es->parent && !XGate.circuit->es->inst)
      editstate_pop(&XGate.circuit->es);
    editstate_setCurrent(XGate.circuit->es);
    FlagRedraw();
    message(0,msgLookup("msg.sim.nogatemod"));	/* Simulation mode requires concrete module stack. */
  }

  SimInterface_setSimMods(si);
  DoTcl("tkg_startSim %s",si->simFileName);	/* Start simulator with specified file */
  si->active = 1;

  FlagRedraw();
}

/*
  Missing cleanup code for simulator module stuff
 */
void SimInterface_end(SimInterface *si)
{
  Circuit_extractProbes(XGate.circuit);
  si->active = 0;
  if (*si->simFileName)
    unlink(si->simFileName);
  DoTcl("tkg_endSim");
  DoTcl("tkg_destroyScope");
  SimInterface_flushBreaks(si);
}

/*****************************************************************************/
/* Simulation script */

SScriptStack *new_SScriptStack(FILE *f)
{
  SScriptStack *SS = (SScriptStack *) ob_malloc(sizeof(SScriptStack),"SScriptStack");

  SS->p = 0;
  SS->f[0] = f;
  SS->ln[0] = 0;
  SS->err_count = 0;
  SS->bptable = new_SHash();

  return SS;
}

void delete_SScriptStack(SScriptStack *SS)
{
  delete_SHash(SS->bptable);
  ob_free(SS);
}

static int simScriptInclude(SScriptStack *SS,char *cmd)
{
  char fileName[STRMAX],x;

  FILE *f;

  if (sscanf(cmd," include \"%[^\"]\" %c",fileName,&x) != 1) {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"include");	/* Syntax error in 'include' command. */
    return -1;
  }

  f = openInPath(fileName);

  if (!f) {
    SS->err_count++;
    message(1,msgLookup("err.sim.noincl"),fileName);	/* Include file '%s' not found. */
    return -1;
  } else {
    SS->f[++SS->p] = f;
    SS->ln[SS->p] = 0;
    return 0;
  }
}

static int simScriptStep(SScriptStack *SS,char *cmd)
{
  int steps;
  char x;

  if (sscanf(cmd," step %d %c",&steps,&x) != 1) {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"step");		/* "Syntax error in 'step' command." */
    return -1;
  }

  DoTcl("tkg_simStep %d\n",steps);
  SS->stop_wait = 1;

  return 0;
}


static int simScriptZoom(SScriptStack *SS,char *cmd)
{
  int zoom;
  char x;

  if (sscanf(cmd," zoom %d %c",&zoom,&x) != 1) {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"zoom");		/* "Syntax error in 'zoom' command." */
    return -1;
  }

  if (zoom > 20) zoom = 20;
  if (zoom < -20) zoom = -20;

  if (zoom > 0) {
    while (zoom-- > 0)
      DoTcl(".scope.main.frame.canvas zoom 1");
  } else if (zoom < 0) {
    while (zoom++ < 0)
      DoTcl(".scope.main.frame.canvas zoom -1");
  }

  return 0;
}

static int simScriptClock(SScriptStack *SS,char *cmd)
{
  char edge;
  int cycles,overStep;
  char clock[STRMAX],x;

  if (sscanf(cmd," clock %c [%[^]]] %d +%d %c",&edge,clock,&cycles,&overStep,&x) == 4) {
  } else if (sscanf(cmd," clock %c [%[^]]] %d %c",&edge,clock,&cycles,&x) == 3) {
    overStep = 0;
  } else if (sscanf(cmd," clock %c %d +%d %c",&edge,&cycles,&overStep,&x) == 3) {
    *clock = 0;
  } else if (sscanf(cmd," clock %c %d %c",&edge,&cycles,&x) == 2) {
    *clock = 0;
    overStep = 0;
  } else {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"clock");	/* Syntax error in 'clock' command. */
    return -1;
  }

  if (edge != '+' && edge != '-') {
    SS->err_count++;
    message(1,msgLookup("err.sim.badedge"),edge);	/* Illegal edge indicator '%c' in clock command. */
    return -1;
  }

  DoTcl("tkg_simCycle %c %d %d %s\n",edge,cycles,overStep,clock);
  SS->stop_wait = 1;

  return 0;
}

static int simScriptRun(SScriptStack *SS,char *cmd)
{
  char x;

  if (sscanf(cmd," run %c",&x) > 0) {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"run");
    return -1;
  }

  DoTcl("tkg_simRun");
  SS->stop_wait = 1;

  return 0;

}
  
static int simScriptBreak(SScriptStack *SS,char *cmd)
{
  char x;
  char bname[STRMAX];
  char expr[STRMAX];
  int id;

  if (sscanf(cmd," break [%[^]]] %[^\n] %c",bname,expr,&x) == 2) {
  } else if (sscanf(cmd," break %[^\n] %c",expr,&x) == 1) {
    *bname = 0;
  } else {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"break");
    return -1;
  }

  DoTcl("tkg_addBP %s",expr);
  SS->stop_wait = 1;

  if (*bname) {
    if (sscanf(XGate.tcl->result,"%d",&id) == 1 && id >= 0) {
      SHash_insert(SS->bptable,bname,(void*)id);
    }
  }

  return 0;
}

static int simScriptDelete(SScriptStack *SS,char *cmd)
{
  char x;
  char bname[STRMAX];
  int i,id;
  SimInterface *si = &XGate.circuit->simulator;
  
  if (sscanf(cmd," delete [%[^]]] %c",bname,&x) != 1) {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"delete");
    return -1;
  }

  id = (int) SHash_find(SS->bptable,bname);

  for (i = 0;i < si->numBPs;i++)
    if (si->breakpoints[i].id == id) break;

  if (i == si->numBPs) {
    message(1,msgLookup("err.sim.nobkpt"),bname);	/* No such breakpoint '%s'. */
    return -1;
  }

  return 0;
}

/*
 * Add or remove a probe on a net
 */
static int simScriptProbe(SScriptStack *SS,char *cmd,int isAdd)
{
  char name[STRMAX],x;
  SimInterface *si = &XGate.circuit->simulator;
  GSimModule *SM;
  GWire *w;

  if (sscanf(cmd," %*s %s %c",name,&x) != 1) {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"probe/unprobe");
    return -1;
  }

  if (SimInterface_lookupWire(si,name,&SM,&w) != 0) {
    SS->err_count++;
    message(1,msgLookup("err.sim.nonet"),name);		/* Can't find net '%s'. */
    return -1;
  }

  if (isAdd) {
    if (!SimInterface_probeExists(si,SM,name))
      SimInterface_addDelProbe(si,SM,name,w,w->nodes,w->nodes->x,w->nodes->y);
  } else {
    if (SimInterface_probeExists(si,SM,name))
      SimInterface_addDelProbe(si,SM,name,w,w->nodes,w->nodes->x,w->nodes->y);
  }

  return 0;
}

/*
 * Set the value of a switch
 */
static int simScriptSet(SScriptStack *SS,char *cmd)
{
  char x;
  char name[STRMAX],svalue[STRMAX],bits[STRMAX],*p;
  SimInterface *si = &XGate.circuit->simulator;
  GSimModule *M;
  GCElement *g;
  GSimSwitch *ss;
  unsigned value;
  int nbits;

  if (sscanf(cmd," set %s %s %c",name,svalue,&x) != 2) {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"set");
    return -1;
  }

  if (SimInterface_lookupGate(si,name,&M,&g,&ss) != 0) {
    SS->err_count++;
    message(1,msgLookup("err.sim.nogate"),name);	/* Can't find gate '%s'. */
    return -1;
  }

  if (sscanf(svalue,"%d'h%x %c",&nbits,&value,&x) == 2) {
  } else if (sscanf(svalue,"%d'd%d %c",&nbits,&value,&x) == 2) {
  } else if (sscanf(svalue,"%d'o%o %c",&nbits,&value,&x) == 2) {
  } else if (sscanf(svalue,"%d'b%s %c",&nbits,bits,&x) == 2) {
    value = 0;
    for (p = bits;*p;p++) {
      if (*p == '1')
	value = (value<<1)|0x1;
      else if (*p == '0')
	value = (value<<1);
      else if (*p != '_') {
	SS->err_count++;
	message(1,msgLookup("err.sim.badbin"),svalue);	/* "Illegal charcter in binary constant '%s'." */
	return -1;
      }
    }
  } else {
    SS->err_count++;
    /* Syntax error in value '%s'.  Must be decimal or verilog-style constant. */
    message(1,msgLookup("err.bkpt.badval"),svalue);
    return -1;
  }

  /*
    Set up overwrite of gate name with wire name
   */
  p = strrchr(name,'.');
  if (p) 
    p++;
  else
    p = name;

  switch (g->typeinfo->Code) {
  case SWITCH :
  case DIP :
    strcpy(p,g->wires[0]->net->signame);
    DoTcl("tkg_simNetSet %s %d'h%x",name,g->wires[0]->net->nbits,value);
    SS->stop_wait = 1;
    if (ss)
      ss->state[0] = value;

    if (M->mod == XGate.circuit->es->env) {		/* Switch is now visible on screen */
      gate_draw(g,GD_NOWIRE);
      g->u.sw.dipval = value;
      gate_draw(g,GD_NOWIRE);
    }
    break;
  case REGISTER :
    break;
  default :
    SS->err_count++;
    message(1,msgLookup("err.sim.notswitch"),name);
    return -1;
  }

  return 0;
}

static int simScriptLoad(SScriptStack *SS,char *cmd)
{
  char x;
  char name[STRMAX],fileName[STRMAX];

  if (sscanf(cmd," load %s \"%[^\"]\" %c",name,fileName,&x) == 2) {
  } else if (sscanf(cmd," load \"%[^\"]\" %c",fileName,&x) == 1) {
    *name = 0;
  } else {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"load");
    return -1;
  }
  
  DoTcl("tkg_simWrite \"memload %s %s\"",fileName,name);
  SS->stop_wait = 1;

  return 0;
}

static int simScriptDump(SScriptStack *SS,char *cmd)
{
  char x;
  char name[STRMAX],fileName[STRMAX];

  if (sscanf(cmd," dump %s \"%[^\"]\" %c",name,fileName,&x) != 2) {
    SS->err_count++;
    message(1,msgLookup("err.sim.syntx"),"dump");
    return -1;
  }
  
  DoTcl("tkg_simDump %s %s",name,fileName);
  SS->stop_wait = 1;

  return 0;
}

static void doScriptCommand(ClientData data)
{
  SScriptStack *SS = (SScriptStack *)data;
  char buf[STRMAX],buf2[STRMAX];
  char *line;

  if (SS->p < 0) return;

  SS->ln[SS->p]++;
  line = fgets(buf,STRMAX,SS->f[SS->p]);

  if (line) {
    char *p = strrchr(buf,'\n');
    if (p) *p=0;

    if (sscanf(buf," %s",buf2) == 1 && *buf2 != '#') {
      if (strcmp(buf2,"include") == 0) {
	simScriptInclude(SS,buf);
      } else if (strcmp(buf2,"step") == 0) {
	simScriptStep(SS,buf);
      } else if (strcmp(buf2,"clock") == 0 || strcmp(buf2,"clock+") == 0 || strcmp(buf2,"clock-") == 0) {
	simScriptClock(SS,buf);
      } else if (strcmp(buf2,"run") == 0) {
	simScriptRun(SS,buf);
      } else if (strcmp(buf2,"break") == 0) {
	simScriptBreak(SS,buf);
      } else if (strcmp(buf2,"delete") == 0) {
	simScriptDelete(SS,buf);
      } else if (strcmp(buf2,"set") == 0) {
	simScriptSet(SS,buf);
      } else if (strcmp(buf2,"probe") == 0) {
	simScriptProbe(SS,buf,1);
      } else if (strcmp(buf2,"unprobe") == 0) {
	simScriptProbe(SS,buf,0);
      } else if (strcmp(buf2,"load") == 0) {
	simScriptLoad(SS,buf);
      } else if (strcmp(buf2,"zoom") == 0) {
	simScriptZoom(SS,buf);
      } else if (strcmp(buf2,"dump") == 0) {
	simScriptDump(SS,buf);
      } else {
	SS->err_count++;
	message(1,msgLookup("err.sim.badcmd"),buf);
      }
    }
  }

  if (SS->err_count) {
    while (SS->p >= 0)
      fclose(SS->f[SS->p--]);
    return;
  } else if (!line) {
    fclose(SS->f[SS->p--]);
  }

  if (SS->p < 0) {
    delete_SScriptStack(SS);
    XGate.circuit->simulator.script = 0;
  } else if (!SS->stop_wait) {
    Tk_DoWhenIdle(doScriptCommand,SS);
  }
}

void SimInterface_execSimScript(SimInterface *si,FILE *f)
{
  SScriptStack *SS = new_SScriptStack(f);

  XGate.circuit->simulator.script = SS;
  Tk_DoWhenIdle(doScriptCommand,SS);
}


