/*
 * Electric(tm) VLSI Design System
 *
 * File: simtexsim.c
 * Simulation aid: TEXSIM output generator
 * Written by: T.J.Goodman, University of Canterbury, N.Z.
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) 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.
 *
 * Electric(tm) 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 Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "config.h"
#if SIMAID

#include "global.h"
#include "efunction.h"
#include "sim.h"
#include "tecschem.h"

#define MAXLENGTH 80
#define MAXNAMECHARS 12
#define TRUE 1
#define FALSE 0
#define NPGATENOR -NPGATEOR
#define NPGATENXOR -NPGATEXOR
#define NPGATENAND -NPGATEAND
#define NULLSTR ""

#define RESFILE "reservedwords.dat"			/* optional used to check for reserved words */

FILE *sim_texfile, *reservefile;

/* prototypes for local routines */
void sim_writetexcell(NODEPROTO*, INTBIG);
char *sim_textdlpower(NODEINST*, INTBIG, INTBIG*, INTBIG*, INTBIG*);
INTSML sim_texcountwires(NODEINST*);
char *sim_texsrcname(NODEINST*, PORTARCINST*);
char *sim_texwritesignals(NODEINST*, INTBIG, INTBIG*);
char *sim_texwriteffsignals(NODEINST*, INTBIG*);
char *sim_textracesignal(PORTARCINST*, NODEINST*);
void sim_texwriteinv(PORTARCINST*, char*, char*);
char *sim_textdlinst(NODEINST*, INTBIG);
void sim_texerror(INTBIG, INTBIG*, char*);
void sim_texstrprint(char*);
char *sim_texname(char*);
char *sim_texproto(NODEINST*, INTBIG, INTBIG*);
char *sim_texffname(NODEINST*, INTBIG*);
char *sim_texgetdelay(NODEINST*, INTBIG);
INTSML sim_texnegatedoutput(NODEINST*);
char *sim_texlibname(NODEPROTO*);
PORTARCINST *sim_oppendpai(PORTARCINST*);
NODEINST *sim_oppendni(PORTARCINST*);
INTSML sim_texcheckname(char*);
void sim_texcompile(NODEPROTO*, INTBIG*);
void sim_texfindmodule(char*, char*, INTBIG*);
INTBIG sim_strcspn(char *str, char *set);

void sim_writetexnetlist(NODEPROTO *np)
{
	NODEPROTO *lnp;
	REGISTER LIBRARY *lib;
	INTBIG length;
	char str1[30], filename[200], *libname, *resfilename, *truename;

	/* write the "TDL" file */
	(void)strcpy(filename, np->cell->cellname);
	(void)strcat(filename, ".tdl");
	sim_texfile = xcreate(filename, sim_filetypetegas, _("TEGAS File"), &truename);
	if (sim_texfile == NULL)
	{
		if (truename != 0) ttyputerr(_("Cannot write %s"), truename);
		return;
	}

	/* read list of reserved words into an infinite string, each separated by "\n" */
	reservefile = xopen(RESFILE, sim_filetypetegastab, "", &resfilename);

	sim_texstrprint("/* GENERATED BY THE ELECTRIC VLSI DESIGN SYSTEM */\n\n");
	sim_texstrprint("COMPILE  ;\n\n");

	/* get library name, check validity */
	strcpy(str1, "DIRECTORY:  ");
	libname = sim_texlibname(np);

	length = strlen(libname);
	if (length > 12)
	{
		ttyputmsg(_("Library name exceeds 12 characters, The name used for the"));
		ttyputmsg(_("TDL directory will be truncated to :- %s"), sim_texname(libname));
	}

	/* check library name */
	if (sim_texcheckname(sim_texname(libname)))
		strcat(str1, sim_texname(libname)); else
	{
		ttyputerr(_("%s IS A RESERVED WORD, RENAME LIBRARY AND RE-RUN"), sim_texname(libname));
		return;
	}

	/* write "directory line" to file */
	sim_texstrprint(str1);
	xprintf(sim_texfile, " ;\n\n");

	sim_texstrprint("OPTIONS:   REPLACE  ;\n\n");

	/* reset flags for cells that have been written */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(lnp = lib->firstnodeproto; lnp != NONODEPROTO; lnp = lnp->nextnodeproto)
			lnp->temp1 = lnp->temp2 = 0;

	/* write TDL descriptions into file */
	sim_writetexcell(np, 1);

	sim_texstrprint(" END COMPILE;\n\n ");

	/* close files */
	if (reservefile != NULL) xclose(reservefile);
	xclose(sim_texfile);

	ttyputmsg(_("%s written"), truename);
}

void sim_writetexcell(NODEPROTO *np, INTBIG top)
{
	PORTPROTO *pp, *xpp;
	NODEINST *ni, *nextni;
	ARCINST *ai;
	char str1[MAXLENGTH], *extra, *ptr;
	INTBIG inputs, nextniinputs, ninegated, nextninegated, f, x, separator,
		count, length, tempa, tempb, module_err=0;
	INTBIG pwr=0, gnd=0;

	/* recurse on sub-cels first */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->proto->primindex != 0) continue;
		if (ni->proto->temp1 != 0) continue;

		sim_writetexcell(ni->proto, 0);
	}

	/* mark this node written */
	np->temp1++;

	/********************* MODULE ***************************************/
	sim_texstrprint("MODULE:  ");
	sim_texstrprint(sim_texname(describenodeproto(np)));
	sim_texstrprint(";\n\n");

	/**************************** INPUTS ***********************************/
	if (np->firstportproto != NOPORTPROTO)
	{
		(void)initinfstr();
		addstringtoinfstr("INPUTS:\n");

		for(pp=np->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
		{
			if (sim_texcheckname(sim_texname(pp->protoname)) == 0)
				sim_texerror(11, &module_err, sim_texname(pp->protoname));

			pp->temp1 = pp->temp2 = 0;
			if ((pp->userbits&STATEBITS) == INPORT)
			{
				addstringtoinfstr("   ");
				addstringtoinfstr(sim_texname(pp->protoname));
				addstringtoinfstr("\n");
				pp->temp1 = 1;
			}
		}
		addstringtoinfstr(";\n\n");
		sim_texstrprint(returninfstr());

		/*********************** OUTPUTS ****************************************/
		(void)initinfstr();
		addstringtoinfstr("OUTPUTS:\n");

		for(pp=np->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto)
		{
			if ((pp->userbits&STATEBITS) == OUTPORT)
			{
				addstringtoinfstr("   ");
				addstringtoinfstr(sim_texname(pp->protoname));
				addstringtoinfstr("\n");
				pp->temp1 = 1;
			}
			if (pp->temp1 == 0) sim_texerror(2, &module_err, pp->protoname);
		}
		addstringtoinfstr(";\n\n");
		sim_texstrprint(returninfstr());
	}

	/******************* USE ******************************************/
	(void)initinfstr();
	(void)addstringtoinfstr("USE:\n\n");  /* write USE: */

	for(ni=np->firstnodeinst, ni->temp2=0; ni!=NONODEINST; ni=ni->nextnodeinst)
	{
		if (ni->temp1 != 0) continue;
		ni->temp1++;	     /* mark node seen */
		f = nodefunction(ni, &extra);

		switch(f)
		{
			case NPGATEAND:
			case NPGATEOR:
			case NPGATEXOR:
				/* Determine whether node should be NAND, NOR or NXOR */
				if (sim_texnegatedoutput(ni))
				{
					x = -f;
					ninegated = TRUE;
				} else
				{
					ninegated = FALSE;
					x = f;
				}

				/* Count number of inputs */
				if ((inputs = sim_texcountwires(ni)) < 2)
					sim_texerror(9, &module_err, sim_texname(describenodeproto(ni->proto)));
				for(nextni=ni->nextnodeinst; nextni!=NONODEINST; nextni=nextni->nextnodeinst)
				{
					/* look at similar nodes */
					if (nextni->proto != ni->proto) continue;

					/* Determine whether next node should be NAND, NOR or NXOR */
					if (sim_texnegatedoutput(nextni))
						nextninegated = TRUE;    /* negated port/arc ? */
							else nextninegated = FALSE;

					/* Count number of inputs on next node*/
					nextniinputs = sim_texcountwires(nextni);

					/* Compare current node and next node, If next node is the same  */
					/* mark it seen */
					if (nextniinputs == inputs && ninegated==nextninegated)
						nextni->temp1++;
				}

				/* Write USE description for current node */
				switch (x)
				{
					case NPGATEAND:
						(void)sprintf(str1, "  %ld-AND = AND(%ld,1),\n", inputs, inputs);
						break;
					case NPGATEOR:
						(void)sprintf(str1, "  %ld-OR = OR(%ld,1),\n", inputs, inputs);
						break;
					case NPGATEXOR:
						(void)sprintf(str1, "  %ld-XOR = XOR(%ld,1),\n", inputs, inputs);
						break;
					case NPGATENAND:
						(void)sprintf(str1, "  %ld-NAND = NAND(%ld,1),\n", inputs, inputs);
						break;
					case NPGATENOR:
						(void)sprintf(str1, "  %ld-NOR = NOR(%ld,1),\n", inputs, inputs);
						break;
					case NPGATENXOR:
						(void)sprintf(str1, "  %ld-NXOR = NXOR(%ld,1),\n", inputs, inputs);
						break;
				}
				(void)addstringtoinfstr(str1);
				break; /* end of switch option */

			case NPSOURCE:
			case NPSOURCEV:
			case NPSOURCEC:
			case NPSOURCECM:
			case NPSOURCET:
			case NPSOURCEDC:
			case NPSOURCEAC:
			case NPSOURCEN:
			case NPSOURCEX:
			case NPSOURCEB:
			case NPSOURCES:
			case NPRESIST:
			case NPCAPAC:
			case NPDIODE:
			case NPINDUCT:
			case NPMETER:
				sim_texerror(4, &module_err, sim_texname(describenodeproto(ni->proto)));
				break;

			/*  This case can either be for an existing  user defined module in this */
			/*  directory or for a module from the MASTER directory */
			case NPUNKNOWN:
				(void)addstringtoinfstr("  ");
				(void)addstringtoinfstr(sim_texproto(ni, f, &module_err));
				(void)addstringtoinfstr(" = ");
				(void)addstringtoinfstr(sim_texproto(ni, f, &module_err));
				(void)addstringtoinfstr("///");
				length = strlen(describenodeproto(ni->proto));

				/* if there is a separator, directory name comes after it */
				if ((separator = sim_strcspn(describenodeproto(ni->proto), "/")) != length)
					(void)addstringtoinfstr(sim_texname(
						describenodeproto(ni->proto) + separator + 1)); else

				/* no separator, hence directory name = library name */
				(void)addstringtoinfstr(sim_texlibname(ni->proto));
				(void)addstringtoinfstr(",\n");

				/* if another node is the same as this one mark it seen */
				for(nextni=ni->nextnodeinst; nextni!=NONODEINST; nextni=nextni->nextnodeinst)
					if (nextni->proto == ni->proto) nextni->temp1++;

				break; /* from  switch */
			default: break;
		} /* end of switch */
	} /* end of for */

	/* get USE string, replace last ',' with a ';' and write to TDL file */
	ptr = returninfstr();
	length = strlen(ptr);
	*(ptr+length-2) = ';';
	sim_texstrprint(ptr);
	sim_texstrprint("\n");

	/*************************  DEFINE  ***********************************/
	sim_texstrprint("DEFINE:\n");

	/* count no. of inverters (negated arcs not attached to logic primitives */
	count = 1;
	for (ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if ((ai->userbits&ISNEGATED) == 0) continue;
		if ((ai->end[0].portarcinst->proto->userbits&STATEBITS) == OUTPORT)
		{
			f = nodefunction(ai->end[0].nodeinst, &extra);
			if (f == NPGATEAND || f == NPGATEOR || f == NPGATEXOR || f == NPBUFFER)
				ai->temp1 = -1; else ai->temp1 = count++;
		} else if ((ai->end[1].portarcinst->proto->userbits&STATEBITS)==OUTPORT)
		{
			f = nodefunction(ai->end[1].nodeinst,&extra);
			if (f == NPGATEAND || f == NPGATEOR || f == NPGATEXOR || f == NPBUFFER)
				ai->temp1 = -1; else ai->temp1 = count++;
		} else ai->temp1 = count++;

		/* Check to ensure at least one end of negated arc is attached to functional */
		/* node */
		tempb = ai->end[1].portarcinst->proto->userbits&STATEBITS;
		tempa = ai->end[0].portarcinst->proto->userbits&STATEBITS;

		if ((tempa&(INPORT|OUTPORT)) == 0 && (tempb&(INPORT|OUTPORT)) == 0)
			sim_texerror(3, &module_err, NULLSTR);
	}

	for (ni=np->firstnodeinst, count=1; ni!=NONODEINST; ni=ni->nextnodeinst)
	{
		ni->temp1 = 0;
		f = nodefunction(ni,&extra);

		/* handle nodeinst descriptions */
		switch(f)
		{
			case NPPIN:
			case NPART:
				break;

			case NPCONPOWER:
			case NPCONGROUND:
				sim_texstrprint(sim_textdlpower(ni, f, &pwr, &gnd, &module_err));
				break;

			case NPTRANS:
			case NPGATEXOR:
			case NPGATEAND:
			case NPGATEOR:
			case NPBUFFER:
			case NPFLIPFLOP:
			case NPUNKNOWN:
				ni->temp2 = count++;
				strcpy(str1, sim_textdlinst(ni, f));
				strcat(str1, " = ");
				strcat(str1, sim_texproto(ni, f, &module_err));
				strcat(str1, sim_texwritesignals(ni, f, &module_err));
				strcat(str1, sim_texgetdelay(ni, f));
				sim_texstrprint(str1);
				sim_texstrprint(";\n");
				break;

			default:
				sim_texerror(4, &module_err, describenodeproto(ni->proto));
				break;
		}
	}

	/* any ports on the same network in the cell have to be made equivalent points */
	/* in the tdl description */
	for(pp=np->firstportproto; pp!= NOPORTPROTO; pp=pp->nextportproto)
	{
		if (pp->temp2 != 0) continue;
		for(xpp=pp->nextportproto; xpp!=NOPORTPROTO; xpp=xpp->nextportproto)
		{
			if (xpp->network != pp->network) continue;
			if (xpp->temp2 != 0) continue;
			sim_texstrprint(sim_texname(pp->protoname));
			sim_texstrprint(" &= ");
			sim_texstrprint(sim_texname(xpp->protoname));
			sim_texstrprint(";\n\n");
			xpp->temp2++;
		}
		pp->temp2++;
	}

	/* end module */
	sim_texstrprint(" END MODULE;\n\n");

	/* if compiled correctly then reset variable */
	if (module_err != 0) ttyputerr(_("*** Errors in module %s"),
		sim_texname(describenodeproto(np)));
}

char *sim_textdlpower(NODEINST *ni, INTBIG f, INTBIG *pwr, INTBIG *gnd, INTBIG *module_err)
{
	static char str[20];

	/* To prevent Un-connected power nodes */
	if (ni->firstportarcinst == NOPORTARCINST)
	{
		sim_texerror(7, module_err, NULLSTR);
		return("");
	}

	if (f == NPCONGROUND && *gnd != 1)
	{
		sprintf(str, "NET%ld = GRND;\n", (INTBIG)ni->firstportarcinst->conarcinst->network);
		*gnd = 1;
		return(str);
	}

	if (f == NPCONPOWER && *pwr != 1)
	{
		sprintf(str, "NET%ld = PWR;\n", (INTBIG)ni->firstportarcinst->conarcinst->network);
		*pwr = 1;
		return(str);
	}

	return("");
}

/* routine to count the wires on a primitive with only one inport */
INTSML sim_texcountwires(NODEINST *ni)
{
	INTSML inputs;
	PORTARCINST *pai;
	PORTEXPINST *pei;

	for(inputs=0, pai=ni->firstportarcinst; pai!=NOPORTARCINST; pai=pai->nextportarcinst)
		if (pai->proto == ni->proto->firstportproto) inputs++;

	for(pei=ni->firstportexpinst; pei!=NOPORTEXPINST; pei=pei->nextportexpinst)
		if (pei->proto == ni->proto->firstportproto) inputs++;

	return(inputs);
}

/*
 * a routine to return the name of the network which may be net<x> or the
 * name of an export on the net
 */
char *sim_texsrcname(NODEINST *ni, PORTARCINST *pai)
{
	PORTPROTO *pp;
	INTBIG exp_found=0;
	static char str[MAXNAMECHARS + 1];

	for(pp=ni->parent->firstportproto; pp != NOPORTPROTO; pp=pp->nextportproto)
	{
		if (pp->network == pai->conarcinst->network)
		{
			sprintf(str, "%s", sim_texname(pp->protoname));
			exp_found++;
			break;
		}
	}
	if (exp_found == 0)
		sprintf(str, "NET%ld", (INTBIG)pai->conarcinst->network);

	return(str);
}

/* a routine to write the sources of signals on the input ports of a nodeinst */
char *sim_texwritesignals(NODEINST *ni, INTBIG f, INTBIG *module_err)
{
	PORTPROTO *pp;
	PORTARCINST *pai;
	PORTEXPINST *pei;
	INTBIG portwired=0, length;
	char *string;

	if (f == NPFLIPFLOP)
		return(sim_texwriteffsignals(ni, module_err));

	if (ni->proto->firstportproto != NOPORTPROTO)
	{
		(void)initinfstr();
		(void)addstringtoinfstr("(");
	} else return(NULLSTR);

	for(pp=ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if((pp->userbits&STATEBITS) != INPORT) continue;

		/* The transistor "s" port is treated as an inport by electric but is used */
		/* as an outport by TDL */
		if (f == NPTRANS && namesamen(pp->protoname, "s", 1) == 0) continue;

		/* Buffer primitive has a "c" port not used by TDL */
		if (f == NPBUFFER && *pp->protoname == 'c') continue;

		portwired = 0;
		for(pai = ni->firstportarcinst; pai != NOPORTARCINST; pai = pai->nextportarcinst)
		{
			if (pai->proto == pp)
			{
				(void)addstringtoinfstr(sim_textracesignal(pai, ni));
				(void)addstringtoinfstr(",");
				portwired++;

				/* if port is not isolated then write one signal only */
				if ((pp->userbits&PORTISOLATED) == 0) break;
			}
		}
		for(pei=ni->firstportexpinst; pei != NOPORTEXPINST; pei=pei->nextportexpinst)
		{
			if (pei->proto == pp)
			{
				(void)addstringtoinfstr(sim_texname(pei->exportproto->protoname));
				(void)addstringtoinfstr(",");
				portwired++;
			}
		}
		if (portwired == 0)
		{
			sim_texerror(8, module_err, pp->protoname);
			(void)addstringtoinfstr("NC,");
		}
	}
	string = returninfstr();
	if (portwired == 0) return(NULLSTR);

	/* replace last ',' with a ')' */
	length = strlen(string);
	*(string + length - 1) = ')';
	*(string+length) = '\0';
	return(string);
}

/*
 * routine to write the signals in the correct pin order for TDL for flip
 * flops n.b this is essentially the same as sim_texwritesignals
 */
char *sim_texwriteffsignals(NODEINST *ni, INTBIG *module_err)
{
	PORTPROTO *pp;
	PORTARCINST *pai;
	PORTEXPINST *pei;
	VARIABLE *var;
	char signals[5][MAXNAMECHARS + 1], *string;
	char *ptr;
	INTBIG x, length;

	for(pp = ni->proto->firstportproto, x=0; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if((pp->userbits&STATEBITS) == OUTPORT) continue;
		ptr = &signals[x++][0];
		strcpy(ptr, "NC,");   /* if no-connection write NC */

		for(pai = ni->firstportarcinst; pai != NOPORTARCINST; pai = pai->nextportarcinst)
		{
			if (pai->proto == pp)
			{
				strcpy(ptr,sim_textracesignal(pai, ni));
				strcat(ptr, ",");
				break;
			}
		}
		for(pei=ni->firstportexpinst; pei != NOPORTEXPINST; pei=pei->nextportexpinst)
		{
			if (pei->proto == pp)
			{
				strcpy(ptr, sim_texname(pei->exportproto->protoname));
				strcat(ptr, ",");
				break;
			}
		}
	}

	/* We now have the signals in 5 arrays ready to be output in the correct */
	/* order which is 2,0,1,3,4.If the flip flop is a D or T type don't put */
	/* out array[1][]. */
	var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, sch_flipfloptypekey);

	(void)initinfstr();
	(void)addstringtoinfstr("(");
	(void)addstringtoinfstr(signals[2]);
	(void)addstringtoinfstr(signals[0]);

	/* JK and SR have one input more than D or T flip flops */
	if (namesamen((char *)var->addr, "jk", 2) == 0 || namesamen((char *)var->addr, "rs", 2) == 0)
		(void)addstringtoinfstr(signals[1]);

	(void)addstringtoinfstr(signals[3]);
	(void)addstringtoinfstr(signals[4]);

	string = returninfstr();
	length = strlen(string);
	*(string+length-1) = ')';
	return(string);
}

/*
 * routine to trace back to the output source supplying a signal to the
 * portarcinst of a nodeinst
 */
char *sim_textracesignal(PORTARCINST *pai, NODEINST *ni)
{
	static char str[MAXNAMECHARS];

	if ((pai->conarcinst->userbits&ISNEGATED) == 0) return(sim_texsrcname(ni, pai));

	/* insert an inverter description if a negated arc attached to primitive */
	/* other than AND,OR,XOR */
	if (pai->conarcinst->temp1 != -1)
	{
		sprintf(str, "I%ld.O", pai->conarcinst->temp1);
		sim_texwriteinv(pai, str, sim_texsrcname(ni, pai));
		return(str);
	}
	return(sim_texsrcname(ni, pai));
}

/* write out an inverter description for a negated arc */
void sim_texwriteinv(PORTARCINST *pai, char *str1, char *str2)
{
	static char invstring[MAXNAMECHARS * 4];
	char tempstr[MAXNAMECHARS];

	if (pai->conarcinst->temp1 == -1) return;

	strcpy(tempstr, sim_texname(str1));
	sprintf(invstring, "I%ld(%s) = NOT(%s);\n", pai->conarcinst->temp1, tempstr, sim_texname(str2));
	sim_texstrprint(invstring);
	pai->conarcinst->temp1 = -1; /* write once only */
}

/*
 * routine to return a string with the TDL names of the output sources from
 * this nodeinst. if neccessary arcs that are negated will have inverter
 * descriptions written.
 */
char *sim_textdlinst(NODEINST *ni, INTBIG f)
{
	NODEINST *xni;
	PORTARCINST  *xpai, *ppai, *pai;
	PORTPROTO   *pp;
	PORTEXPINST *pei;
	char str1[5], str2[MAXNAMECHARS + 10], *netstr;
	INTBIG length, portcount=0, instcount=0;

	sprintf(str1, "U%ld", ni->temp2);
	(void)initinfstr();
	(void)addstringtoinfstr(str1);
	(void)addstringtoinfstr("(");

	for(pp=ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (((pp->userbits&STATEBITS) == INPORT) && f != NPTRANS) continue;
		if (f == NPTRANS && namesamen(pp->protoname, "g", 1) == 0) continue;

		portcount++;
		for(pai=ni->firstportarcinst; pai!=NOPORTARCINST; pai=pai->nextportarcinst)
		{
			if (pai->proto != pp) continue;
			if (portcount == instcount) continue;
			instcount++;

			/* if arc on port not negated or a description for an inverter for a negated */
			/* arc already exists simply write the source name of this port/arc inst.*/

			if ((pai->conarcinst->userbits&ISNEGATED) == 0 || pai->conarcinst->temp1 == -1)
			{
				(void)addstringtoinfstr(sim_texsrcname(ni, pai));
				(void)addstringtoinfstr(",");
				break;
			} else

			/* if the negation is at this end (ie nearest this node) write an inverter */
			/* with the port name of the output as inverter input and the net name of the */
			/* arc as inverter output. The source name is the net name of the arc */
			if ((pai->conarcinst->userbits&REVERSEEND) == 0)
			{
				sprintf(str2, "U%ld.%s", ni->temp2, sim_texname(pai->proto->protoname));
				(void)addstringtoinfstr(str2);
				(void)addstringtoinfstr(",");
				sim_texwriteinv(pai, sim_texsrcname(ni, pai), str2);
			} else

			/* if the negated arc is reversed get the name of the port/arc instance at the*/
			/* opposite end. */
			if ((pai->conarcinst->userbits&REVERSEEND) != 0)
			{
				xpai = sim_oppendpai(pai);
				xni = sim_oppendni(pai);

				/* if the port at the opposite end is an input port use the net name. The */
				/* handling of the negated arc will be done by sim_textracesignal() */
				if ((xpai->proto->userbits&STATEBITS) ==INPORT)
				{
					(void)addstringtoinfstr(sim_texsrcname(ni, pai));
					(void)addstringtoinfstr(",");
				} else
				{
					/* if the port at the opposite end is not an inport, look at all other    */
					/* port/arc instances. If one of these is on a new net, insert an    */
					/* inverter description with the input to the inverter the current net name */
					/* and the output the new net name. The source name will be the new net name */
					for(ppai = xni->firstportarcinst; ppai != NOPORTARCINST; ppai = ppai->nextportarcinst)
					{
						if (ppai == xpai) continue;
						if (ppai->conarcinst->network != pai->conarcinst->network)
						{
							netstr = sim_texsrcname(xni, ppai);
							break;
						}
					}
					(void)addstringtoinfstr(netstr);
					(void)addstringtoinfstr(",");
					sim_texwriteinv(pai, netstr, sim_texsrcname(ni, pai));
				} /* end of else*/
			}/* end of else*/
		} /* end of for */

		for(pei=ni->firstportexpinst; pei!=NOPORTEXPINST; pei=pei->nextportexpinst)
		{
			if (pei->proto != pp) continue;
			if (portcount == instcount) continue;
			instcount++;
			(void)addstringtoinfstr(sim_texname(pei->exportproto->protoname));
			(void)addstringtoinfstr(",");
			break;
		}
		if (instcount < portcount) (void)addstringtoinfstr("NC,");
	}
	netstr = returninfstr();
	if (portcount == 0) return(NULLSTR);
	length = strlen(netstr);
	*(netstr+length-1) = ')';
	return(netstr);
}

/* To handle all error conditions */
void sim_texerror(INTBIG error, INTBIG *lasterror, char *str)
{
	char *msg;

	(void)initinfstr();
	switch (error)
	{
		case 1:
			(void)addstringtoinfstr(_("UNABLE TO OPEN TDL FILE"));
			break;
		case 2:
			(void)formatinfstr(_("EXPORT %s MUST BE EITHER INPUT OR OUTPUT"), str);
			break;
		case 3:
			(void)addstringtoinfstr(_("NEGATED ARC MUST BE CONNECTED TO INPUT OR OUTPUT OF NODEINST"));
			break;
		case 4:
			(void)formatinfstr(_("NODETYPE %s NOT SUPPORTED"), str);
			break;
		case 5:
			(void)addstringtoinfstr(_("UNABLE TO OPEN TDL LISTFILE"));
			break;
		case 6:
			(void)addstringtoinfstr(_("ERROR IN TDL LISTFILE"));
			break;
		case 7:
			(void)addstringtoinfstr(_("PWR / GND NODE UNCONNECTED"));
			break;
		case 8:
			(void)formatinfstr(_("UNWIRED PORT %s"), str);
			break;
		case 9:
			(void)formatinfstr(_("ONLY ONE INPUT ON %s"), str);
			break;
		case 10:
			(void)addstringtoinfstr(_("T TYPE FLIP-FLOP MUST BE MS"));
			break;
		case 11:
			(void)formatinfstr(_("%s IS A RESERVED WORD"), str);
			break;
	}

	msg = returninfstr();
	sim_texstrprint(_("\n/************* ERROR **************/\n/*** "));
	sim_texstrprint(msg);
	sim_texstrprint(" ***\n\n");
	ttyputerr("%s", msg);
	*lasterror = error;
}

/*
 * this routine outputs TDL to the .TDL file. the maximum line length in a
 * TDL file is 80 chars. this routine ensures that no lines exceed this
 * it searches for the last space character and inserts a new line character
 * at that point
 */
void sim_texstrprint(char *str)
{
	INTBIG length, charcount;
	char *start_ptr, *max_line_ptr, newline[2];
	char outstring[MAXLENGTH+3];

	start_ptr = str;
	strcpy(newline, "\n");
	length = strlen(start_ptr);

	while (*start_ptr != '\0')
	{
		/* no new line chars and length > MAX */
		if (length > MAXLENGTH && (!strchr(start_ptr, '\n')))
		{
			max_line_ptr = start_ptr + MAXLENGTH;
			charcount = MAXLENGTH;

			/* find a suitable place to break string */
			while (!(*max_line_ptr == ',' || *max_line_ptr == ' '))
			{
				charcount -=1; max_line_ptr -=1;
				if (max_line_ptr == start_ptr)   /* no suitable break point */
				{
					max_line_ptr = start_ptr + MAXLENGTH;
					charcount = MAXLENGTH;
					break;
				}
			}

			/* copy string from start up to break point (max_line_ptr) */
			strncpy(outstring, start_ptr, charcount + 1);
			strcat(outstring, newline);
			xprintf(sim_texfile, "%s", outstring);
			start_ptr = max_line_ptr + 1;
			length = strlen(start_ptr);
		} else
		{
			xprintf(sim_texfile, "%s", start_ptr);
			break;
		}
	}
}

/* returns a string of max 12 chars and in upper case for TDL */
char *sim_texname(char *str)
{
	static char buffer[MAXNAMECHARS +1], letter;
	char tempbuffer[MAXNAMECHARS +1];
	INTBIG i, length;

	if ((length=strlen(str)) > MAXNAMECHARS) length = MAXNAMECHARS;
	strncpy(tempbuffer, str, length);
	for(i=0; i != length; i++)
	{
		letter = tempbuffer[i];
		buffer[i] = toupper(letter);
	}
	buffer[length] = '\0';
	return(buffer);
}

/*
 * routine to return the TDL name of a nodeinst
 * this routine will strip out a TDL directory name if present
 */
char *sim_texproto(NODEINST *ni, INTBIG f, INTBIG *module_err)
{
	INTBIG x, length, span;
	static char buffer[MAXNAMECHARS];

	if (f == NPGATEAND || f == NPGATEOR || f == NPGATEXOR)
	{
		if((x=sim_texcountwires(ni)) > 1) sprintf(buffer, "%ld-", x); else
		{
			sim_texerror(9, module_err, describenodeproto(ni->proto));
			sprintf(buffer, "2-");
		}

		if (sim_texnegatedoutput(ni)) strcat(buffer, "N");
	} else strcpy(buffer, "");

	switch(f)
	{
		case NPGATEAND:
			strcat(buffer, "AND");
			return(buffer);
		case NPGATEOR:
			strcat(buffer, "OR");
			return(buffer);
		case NPGATEXOR:
			strcat(buffer, "XOR");
			return(buffer);
		case NPBUFFER:
			if (sim_texnegatedoutput(ni)) strcat(buffer, "NOT");
				else strcat(buffer, "DELAY");
			return(buffer);
		case NPFLIPFLOP:
			return(sim_texffname(ni, module_err));
		case NPTRANS:
			strcat(buffer, "BDSWITCH");
			return(buffer);
		case NPUNKNOWN:
			length = strlen(describenodeproto(ni->proto));
			if ((span = sim_strcspn(describenodeproto(ni->proto), "/")) != length)
			{
				if (span > MAXNAMECHARS) span = MAXNAMECHARS;
				strncpy(buffer, describenodeproto(ni->proto), span);
				return(sim_texname(buffer));
			}
			return(sim_texname(describenodeproto(ni->proto)));
		default:
			 return(buffer); /* for now */
	}
}

/* routine to return the TDL primitive name of a flip-flop in electric */
char *sim_texffname(NODEINST *ni, INTBIG *module_err)
{
	VARIABLE *var;
	char *str;
	static char name[5];

	var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, sch_flipfloptypekey);

	for (str = (char *)var->addr; str != 0; str++)
	{
		if (namesamen(str, "rs", 2)== 0)
		{
			strcpy(name, "SR");
			str++;
			continue;
		}
		if (namesamen(str, "jk", 2)== 0)
		{
			strcpy(name, "JK");
			str++;
			continue;
		}
		if (namesamen(str, "t", 1)== 0)
		{
			str++;
			if (*str == 'p' || *str == 'n') sim_texerror(10, module_err, NULLSTR);
			strcpy(name, "TMNE");
			break;
		}
		if (namesamen(str, "d", 1)== 0)
		{
			strcpy(name, "D");
			str++;
			continue;
		}
		if (namesamen(str, "p", 1)== 0)
		{
			strcat(name, "EPE");
			break;
		}
		if (namesamen(str, "n", 1)== 0)
		{
			strcat(name, "ENE");
			break;
		}
		if (namesamen(str, "ms", 2)== 0)
		{
			strcat(name, "MNE");
			break;
		}
	}
	return(name);
}

/*
 * this routine returns a string containing the TDL delay. if no delay variable
 * present the default "/1,1/" is returned
 */
char *sim_texgetdelay(NODEINST *ni, INTBIG f)
{
	VARIABLE *var;
	static char str1[30], str2[30];
	static INTBIG SIM_rise_delay = 0, SIM_fall_delay = 0;

	/* user defined modules are not allowed delays */
	if (f == NPUNKNOWN) return("");

	/* initialise variable for signal source names to be held on portarcinsts */
	/* and TEX rise and fall time delays */
	if (SIM_rise_delay == 0) SIM_rise_delay = makekey("SIM_rise_delay");
	if (SIM_fall_delay == 0) SIM_fall_delay = makekey("SIM_fall_delay");

	var = getvalkey((INTBIG)ni, VNODEINST, -1, SIM_rise_delay);
	if (var == NOVARIABLE) sprintf(str1, "/1,"); else
	{
		if ((var->type&VTYPE) == VINTEGER) sprintf(str1, "/%1ld,", var->addr);
		if ((var->type&VTYPE) == VSTRING) sprintf(str1, "/%s,", (char *)var->addr);
	}

	var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, SIM_fall_delay);
	if (var == NOVARIABLE) sprintf(str2, "1/"); else
	{
		if ((var->type&VTYPE) == VINTEGER) sprintf(str2, "%1ld/", var->addr);
		if ((var->type&VTYPE) == VSTRING) sprintf(str2, "%s/", (char *)var->addr);
	}

	strcat(str1, str2);
	return(str1);
}

/* determines whether a nodeinst has an output arc that is negated */
INTSML sim_texnegatedoutput(NODEINST *ni)
{
	PORTARCINST *pai;

	for(pai=ni->firstportarcinst; pai!=NOPORTARCINST; pai=pai->nextportarcinst)
	{
		if ((pai->conarcinst->userbits&ISNEGATED) == 0) continue;
		if ((pai->proto->userbits&STATEBITS) == OUTPORT) return(1);
	}
	return(0);
}

/* routine to remove the path specification from the electric library name */
char *sim_texlibname(NODEPROTO *np)
{
	INTBIG separator;

	if (np->primindex != 0) return("MASTER");

	separator = sim_strcspn(np->cell->lib->libname, ":]") + 1;
	if (separator == (INTBIG)(strlen(np->cell->lib->libname)+1)) separator = 0;
	return(sim_texname((np->cell->lib->libname + separator)));
}

/*
 * routine to return the portarcinst at the opposite end of the arc which
 * connects tO pai
 */
PORTARCINST *sim_oppendpai(PORTARCINST *pai)
{
	if (pai->conarcinst->end[0].portarcinst == pai)
		return(pai->conarcinst->end[1].portarcinst);
	return(pai->conarcinst->end[0].portarcinst);
}

/*
 * routine to return the nodeinst at the opposite end of the arc which
 * connects to pai
 */
NODEINST *sim_oppendni(PORTARCINST *pai)
{
	if (pai->conarcinst->end[0].portarcinst == pai)
		return(pai->conarcinst->end[1].nodeinst);
	return(pai->conarcinst->end[0].nodeinst);
}

/*
 * routine that takes a string of max. length 12 and checks to see if it is a
 * reserved word
 */
INTSML sim_texcheckname(char *str)
{
	char name[MAXNAMECHARS], *p, fileline[MAXLENGTH];
	INTBIG i=0, x=0;

	if (reservefile != NULL)
	{
		xseek(reservefile, 0, 0);
		p = fileline;
		while (xfgets(fileline, MAXLENGTH, reservefile) == 0)
		{
			for(i=0; i != MAXNAMECHARS-1; i++) name[i]= 0;
			i = 0;
			while (!(*p == '\n' || *p =='\0')) name[i++] = *p++;

			p = fileline;
			if ((x = strlen(str)) < 2) x = 2;  /* minimum abbrev. is 2 chars */
			if (namesamen(name, str, x) == 0) return(0);
		}
		return(1);
	}
	return(1);
}

/* private version of library function (may not be on all systems) */
INTBIG sim_strcspn(char *str, char *set)
{
	INTBIG pos;
	char *pt;

	for(pos=0; str[pos] != 0; pos++)
	{
		for(pt = set; *pt != 0; pt++)
			if (str[pos] == *pt) break;
		if (*pt != 0) break;
	}
	return(pos);
}

#endif /* SIMAID - at top */
