/*
 * Electric(tm) VLSI Design System
 *
 * File: simalscom.c
 * Asynchronous Logic Simulator command handler
 * From algorithms by: Brent Serbin and Peter J. Gallant
 * Last maintained by: Steven M. Rubin
 *
 * 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 "sim.h"
#include "simals.h"
#include "usrdiacom.h"

/* prototypes for local routines */
void simals_erase_submod(CONPTR);
void simals_print_in_entry(ROWPTR);
void simals_print_out_entry(ROWPTR);
void simals_print_xref_entry(CONPTR, INTBIG);
char *simals_strengthstring(INTSML);
void simals_makeactelmodel(char *name, FILE *io, LIBRARY *lib);
INTSML simals_fgets(char *line, INTSML limit, FILE *file);
void simals_sdfannotate(CONPTR cellhead);
NODEINST *simals_getcellinstance(char *celltype, char *instance);
void simals_sdfportdelay(CONPTR cellhead, NODEINST *ni, char *path);
INTBIG simals_getportdelayvalue(char *datastring, char *transition, DELAY_TYPES delaytype);
INTBIG simals_getdval(char *tstring, DELAY_TYPES delaytype);
void simals_order_save(void);
void simals_order_restore(char *list);
void simals_update_netlist(void);
char *simals_parent_level(char *child);
INTSML simals_get_tdindex(char *name);

/* local variables */
DELAY_TYPES simals_sdfdelaytype;
char *tnames[] = {"01", "10", "0Z", "Z1", "1Z", "Z0", "0X", "X1", "1X", "X0", "XZ", "ZX"};

/****************************** ACTEL MODELS ******************************/

typedef enum {inputparam, outputparam, bidirparam, edgesenseinputparam,
	fourstatebiputparam} PARAMTYPE;

typedef struct Iactelparam
{
	char               *paramname;
	PARAMTYPE           paramtype;
	INTSML              duplicate;
	struct Iactelparam *next;
} ACTELPARAM;

#define MAXCHRS 300

/*
 * Name: simals_build_actel_command
 *
 * Description:
 *	This procedure reads an Actel table file and creates a library with
 * ALS-netlist facets.
 */
void simals_build_actel_command(INTSML count, char *par[])
{
	FILE *io;
	INTSML len, i, filecount;
	char *filename, line[MAXCHRS], **filelist;
	REGISTER char *pt, *start;
	static INTBIG filetypeacteltab = -1;

	if (count == 0)
	{
		ttyputusage("tellaid simulation als build-actel-model TABLEFILE");
		return;
	}
	len = strlen(par[0]);
	for(i=len-1; i>0; i--) if (par[0][i] == DIRSEP) break;
	par[0][i] = 0;
	filecount = filesindirectory(par[0], &filelist);
	if (filecount == 0)
	{
		ttyputerr(_("There are no files in directory '%s'"), par[0]);
		return;
	}
	for(i=0; i<filecount; i++)
	{
		(void)initinfstr();
		(void)addstringtoinfstr(par[0]);
		(void)addtoinfstr(DIRSEP);
		(void)addstringtoinfstr(filelist[i]);
		pt = returninfstr();
		if (filetypeacteltab < 0)
			filetypeacteltab = setupfiletype("tab", "*.tab", MACFSTAG('TEXT'), 0, "actel", _("QuickPartTable"));
		io = xopen(pt, filetypeacteltab, "", &filename);
		if (io == 0)
		{
			ttyputerr(_("Cannot find Actel file %s"), pt);
			break;
		}
		for(;;)
		{
			if (simals_fgets(line, MAXCHRS, io) != 0) break;
			pt = line;
			while (*pt == ' ' || *pt == '\t') pt++;
			if (*pt == 0 || *pt == '#') continue;

			if (namesamen(pt, "model ", 6) != 0) continue;
			pt += 6;
			while (*pt == ' ' || *pt == '\t') pt++;
			start = pt;
			while (*pt != 0 && *pt != ':') pt++;
			if (*pt != 0) *pt = 0;
			simals_makeactelmodel(start, io, el_curlib);
		}
		xclose(io);
	}
}

void simals_makeactelmodel(char *name, FILE *io, LIBRARY *lib)
{
	char line[MAXCHRS];
	REGISTER char *pt, *start, save, *facetname;
	REGISTER NODEPROTO *np;
	INTSML statetablestate, doinginput, edgesense, fourstate;
	ACTELPARAM *ap, *lastap, *inputlist, *outputlist, *paramlist, *paramtail, *nextap, *newap;
	void *sa;

	sa = newstringarray(sim_aid->cluster);
	if (sa == 0) return;

	/* dump header information for the model */
	(void)initinfstr();
	(void)addstringtoinfstr("# Actel model for ");
	(void)addstringtoinfstr(name);
	addtostringarray(sa, returninfstr());

	inputlist = outputlist = paramlist = 0;
	statetablestate = 0;
	for(;;)
	{
		if (simals_fgets(line, MAXCHRS, io) != 0) break;

		/* find the first keyword */
		pt = line;
		while (*pt == ' ' || *pt == '\t') pt++;
		if (*pt == '#') continue;
		if (*pt == 0) continue;

		/* stop if done with table */
		if (namesamen(pt, "end", 3) == 0) break;

		/* see if gathering state table information */
		if (statetablestate != 0)
		{
			if (statetablestate == 1)
			{
				/* get state table header */
				statetablestate = 2;
				doinginput = 1;
				paramtail = 0;
				for(;;)
				{
					while (*pt == ' ' || *pt == '\t') pt++;
					start = pt;
					while (*pt != 0 && *pt != ' ' && *pt != '\t' && *pt != ',' && *pt != ';' &&
						*pt != ':') pt++;
					save = *pt;   *pt = 0;
					if (doinginput != 0)
					{
						/* search input list for this one */
						lastap = 0;
						for(ap = inputlist; ap != 0; ap = ap->next)
						{
							if (strcmp(ap->paramname, start) == 0) break;
							lastap = ap;
						}
						if (ap == 0)
						{
							/* see if it is on the output list */
							for(ap = outputlist; ap != 0; ap = ap->next)
								if (strcmp(ap->paramname, start) == 0) break;
							if (ap == 0)
							{
								ttyputerr(_("In model %s, symbol %s not found in parameter lists"),
									name, start);
								return;
							}
							newap = (ACTELPARAM *)emalloc(sizeof (ACTELPARAM), sim_aid->cluster);
							if (newap == 0) return;
							(void)allocstring(&newap->paramname, ap->paramname, sim_aid->cluster);
							newap->paramtype = ap->paramtype;
							newap->duplicate = 1;
							ap = newap;
						} else
						{
							if (lastap == 0) inputlist = ap->next; else
								lastap->next = ap->next;
						}
					} else
					{
						/* search output list for this one */
						lastap = 0;
						for(ap = outputlist; ap != 0; ap = ap->next)
						{
							if (strcmp(ap->paramname, start) == 0) break;
							lastap = ap;
						}
						if (ap == 0)
						{
							/* see if it is already on the input list */
							for(ap = paramlist; ap != 0; ap = ap->next)
								if (strcmp(ap->paramname, start) == 0) break;
							if (ap == 0)
							{
								ttyputerr(_("In model %s, symbol %s not found in parameter lists"),
									name, start);
								return;
							}
							newap = (ACTELPARAM *)emalloc(sizeof (ACTELPARAM), sim_aid->cluster);
							if (newap == 0) return;
							(void)allocstring(&newap->paramname, ap->paramname, sim_aid->cluster);
							newap->paramtype = ap->paramtype;
							newap->duplicate = 1;
							ap = newap;
						} else
						{
							if (lastap == 0) outputlist = ap->next; else
								lastap->next = ap->next;
						}
					}
					ap->next = 0;
					if (paramtail == 0) paramlist = paramtail = ap; else
					{
						paramtail->next = ap;
						paramtail = ap;
					}
					*pt = save;
					while (*pt == ' ' || *pt == '\t') pt++;
					if (*pt == ';') break;
					if (*pt == ':') { pt += 2;   doinginput = 0;   continue; }
					if (*pt == ',') { pt++;   continue; }
					ttyputerr(_("In model %s, missing comma in Actel symbol list"), name);
					return;
				}

				/* now dump the model header */
				(void)initinfstr();
				(void)addstringtoinfstr("gate ");
				(void)addstringtoinfstr(name);
				(void)addstringtoinfstr("(");
				for(ap = paramlist; ap != 0; ap = ap->next)
				{
					if (ap->duplicate != 0) continue;
					if (ap != paramlist) (void)addstringtoinfstr(", ");
					(void)addstringtoinfstr(ap->paramname);
				}
				(void)addstringtoinfstr(")");
				addtostringarray(sa, returninfstr());
				addtostringarray(sa, "t: delta=1.0e-9");
				continue;
			} else
			{
				/* get state table entry */
				ap = paramlist;
				(void)initinfstr();
				(void)addstringtoinfstr("i: ");
				for(;;)
				{
					while (*pt == ' ' || *pt == '\t') pt++;
					(void)addstringtoinfstr(ap->paramname);
					(void)addstringtoinfstr("=");
					switch (*pt)
					{
						case '0': (void)addstringtoinfstr("L");   break;
						case '1': (void)addstringtoinfstr("H");   break;
						case '?': (void)addstringtoinfstr("X");   break;
						case '[': (void)addstringtoinfstr("E");   break;
						case '(': (void)addstringtoinfstr("F");   break;
						case 'Z': (void)addstringtoinfstr("Z");   break;
						case 'X': (void)addstringtoinfstr("XX");  break;
						case '!': (void)addstringtoinfstr("N");   break;
					}
					(void)addstringtoinfstr(" ");

					while (*pt != 0 && *pt != ',' && *pt != ';' && *pt != ':') pt++;
					if (*pt == ';') break;
					if (*pt == ':') { (void)addstringtoinfstr("o: ");   pt++; }
					pt++;
					ap = ap->next;
				}
				addtostringarray(sa, returninfstr());
				continue;
			}
		}

		/* handle "edge_sense" prefix */
		edgesense = 0;
		if (namesamen(pt, "edge_sense", 10) == 0)
		{
			edgesense = 1;
			pt += 10;
			while (*pt == ' ' || *pt == '\t') pt++;
		}

		/* handle "four_state" prefix */
		fourstate = 0;
		if (namesamen(pt, "four_state", 10) == 0)
		{
			fourstate = 1;
			pt += 10;
			while (*pt == ' ' || *pt == '\t') pt++;
		}

		/* gather inputs */
		if (namesamen(pt, "input", 5) == 0)
		{
			pt += 5;
			for(;;)
			{
				while (*pt == ' ' || *pt == '\t') pt++;
				start = pt;
				while (*pt != 0 && *pt != ' ' && *pt != '\t' && *pt != ',' && *pt != ';') pt++;
				save = *pt;   *pt = 0;
				ap = (ACTELPARAM *)emalloc(sizeof (ACTELPARAM), sim_aid->cluster);
				if (ap == 0) break;
				(void)allocstring(&ap->paramname, start, sim_aid->cluster);
				if (edgesense != 0) ap->paramtype = edgesenseinputparam; else
					ap->paramtype = inputparam;
				ap->duplicate = 0;
				ap->next = inputlist;
				inputlist = ap;
				*pt = save;
				while (*pt == ' ' || *pt == '\t') pt++;
				if (*pt == ';') break;
				if (*pt == ',') { pt++;   continue; }
				ttyputerr(_("In model %s, missing comma in Actel input list"), name);
				return;
			}
			continue;
		}

		/* gather outputs */
		if (namesamen(pt, "output", 6) == 0)
		{
			pt += 6;
			for(;;)
			{
				while (*pt == ' ' || *pt == '\t') pt++;
				start = pt;
				while (*pt != 0 && *pt != ' ' && *pt != '\t' && *pt != ',' && *pt != ';') pt++;
				save = *pt;   *pt = 0;
				ap = (ACTELPARAM *)emalloc(sizeof (ACTELPARAM), sim_aid->cluster);
				if (ap == 0) break;
				(void)allocstring(&ap->paramname, start, sim_aid->cluster);
				ap->paramtype = outputparam;
				ap->duplicate = 0;
				ap->next = outputlist;
				outputlist = ap;
				*pt = save;
				while (*pt == ' ' || *pt == '\t') pt++;
				if (*pt == ';') break;
				if (*pt == ',') { pt++;   continue; }
				ttyputerr(_("In model %s, missing comma in Actel output list"), name);
				return;
			}
			continue;
		}

		/* gather bidirectionals */
		if (namesamen(pt, "biput", 5) == 0)
		{
			pt += 5;
			for(;;)
			{
				while (*pt == ' ' || *pt == '\t') pt++;
				start = pt;
				while (*pt != 0 && *pt != ' ' && *pt != '\t' && *pt != ',' && *pt != ';') pt++;
				save = *pt;   *pt = 0;
				ap = (ACTELPARAM *)emalloc(sizeof (ACTELPARAM), sim_aid->cluster);
				if (ap == 0) break;
				(void)allocstring(&ap->paramname, start, sim_aid->cluster);
				if (fourstate != 0) ap->paramtype = fourstatebiputparam; else
					ap->paramtype = bidirparam;
				ap->duplicate = 0;
				ap->next = inputlist;
				inputlist = ap;
				*pt = save;
				while (*pt == ' ' || *pt == '\t') pt++;
				if (*pt == ';') break;
				if (*pt == ',') { pt++;   continue; }
				ttyputerr(_("In model %s, missing comma in Actel biput list"), name);
				return;
			}
			continue;
		}

		/* start state table */
		if (namesamen(pt, "state_table", 11) == 0)
		{
			statetablestate = 1;
			continue;
		}
	}
	(void)initinfstr();
	(void)addstringtoinfstr("load");
	for(ap = paramlist; ap != 0; ap = nextap)
	{
		nextap = ap->next;
		if (ap->paramtype == inputparam || ap->paramtype == edgesenseinputparam)
		{
			(void)addstringtoinfstr(" ");
			(void)addstringtoinfstr(ap->paramname);
			(void)addstringtoinfstr("=1.0");
		}
		efree(ap->paramname);
		efree((char *)ap);
	}
	addtostringarray(sa, returninfstr());

	/* now put strings onto a facet */
	(void)initinfstr();
	(void)addstringtoinfstr(name);
	(void)addstringtoinfstr("{net-als}");
	facetname = returninfstr();
	np = newnodeproto(facetname, lib);
	if (np == NONODEPROTO)
	{
		ttyputerr(_("Cannot create facet %s"), facetname);
		return;
	}
	stringarraytotextfacet(sa, np, 1);
	killstringarray(sa);
	ttyputmsg(_("Created %s"), describenodeproto(np));
}

INTSML simals_fgets(char *line, INTSML limit, FILE *file)
{
	REGISTER char *pp;
	REGISTER INTSML c, total;

	pp = line;
	total = 1;
	for(;;)
	{
		c = xgetc(file);
		if (c == EOF)
		{
			if (pp == line) return(1);
			break;
		}
		if (c == '\n' || c == '\r') break;
		*pp++ = (char)c;
		if ((++total) >= limit) break;
	}
	return(*pp = 0);
}

/****************************** CLOCK ******************************/

/*
 * Name: simals_clock_command
 *
 * Description:
 *	This procedure enters a complex clock vector into the user defined
 * event list.  The user is polled for the node name and timing parameters
 * before any entry is made into the linklist.
 */
void simals_clock_command(INTSML count, char *par[])
{
	float    time;
	NODEPTR  nodehead;
	NODEPROTO *np;
	LINKPTR  vectptr1, vectptr2, sethead;
	ROWPTR   clokhead;
	float    linear, totaltime;
	LINKPTR  vectroot;
	char     **vectptr;
	INTSML    strength, l, i;
	INTBIG    num;

	if (sim_window_isactive(&np) == 0)
	{
		ttyputerr(_("No simulator active"));
		return;
	}
	if (count < 1)
	{
		ttyputusage("tellaid simulation als clock NODENAME (freq | period | custom)");
		return;
	}

	simals_convert_to_upper(par[0]);
	nodehead = simals_find_node(par[0]);
	if (! nodehead)
	{
		ttyputerr(_("ERROR: Unable to find node %s"), par[0]);
		return;
	}

	if (count < 2)
	{
		count = sim_alsclockdlog(&par[1]) + 1;
		if (count < 2) return;
	}

	/* see if there are frequency/period parameters */
	l = strlen(par[1]);
	if (namesamen(par[1], "frequency", l) == 0 || namesamen(par[1], "period", l) == 0)
	{
		if (count < 3)
		{
			ttyputusage("tellaid simulation als clock NODENAME frequency/period PERIOD");
			return;
		}
		time = (float)atof(par[2]);
		if (time <= 0.0)
		{
			ttyputerr(_("Clock timing parameter must be greater than 0"));
			return;
		}

		if (namesamen(par[1], "frequency", l) == 0) time = 1.0f / time;

		vectptr2 = simals_alloc_link_mem();
		if (vectptr2 == 0) return;
		vectptr2->type = 'N';
		vectptr2->ptr = (char *)nodehead;
		vectptr2->state = LOGIC_HIGH;
		vectptr2->strength = VDD_STRENGTH;
		vectptr2->priority = 1;
		vectptr2->time = 0.0;
		vectptr2->right = 0;

		vectptr1 = simals_alloc_link_mem();
		if (vectptr1 == 0) return;
		vectptr1->type = 'N';
		vectptr1->ptr = (char *)nodehead;
		vectptr1->state = LOGIC_LOW;
		vectptr1->strength = VDD_STRENGTH;
		vectptr1->priority = 1;
		vectptr1->time = time / 2.0f;
		vectptr1->right = vectptr2;

		clokhead = (ROWPTR)simals_alloc_mem((INTBIG)sizeof(ROW));
		if (clokhead == 0) return;
		clokhead->inptr = (IOPTR)vectptr1;
		clokhead->outptr = 0;
		clokhead->delta = time;
		clokhead->linear = 0.0;
		clokhead->exp = 0.0;
		clokhead->abs = 0.0;
		clokhead->random = 0.0;
		clokhead->next = 0;
		clokhead->delay = 0;

		sethead = simals_alloc_link_mem();
		if (sethead == 0) return;
		sethead->type = 'C';
		sethead->ptr = (char *)clokhead;
		sethead->state = 0;
		sethead->priority = 1;
		sethead->time = 0.0;
		sethead->right = 0;
		simals_insert_set_list(sethead);

		simals_initialize_simulator(0);
		return;
	}

	if (namesamen(par[1], "custom", l) != 0)
	{
		ttyputbadusage("tellaid simulation als clock");
		return;
	}

	/* handle custom clock specification */
	if (count < 7)
	{
		ttyputusage("tellaid simulation als clock custom RAN STR CY (L D) *");
		return;
	}

	linear = (float)atof(par[2]);
	strength = (INTSML)simals_atoi(par[3]);
	num = simals_atoi(par[4]);

	totaltime = 0.0;
	vectroot = 0;
	vectptr = (char**) &vectroot;
	for(i=5; i<count; i += 2)
	{
		vectptr2 = simals_alloc_link_mem();
		if (vectptr2 == 0) return;
		vectptr2->type = 'N';
		vectptr2->ptr = (char *) nodehead;
		vectptr2->state = simals_trans_state_to_number(par[i]);
		vectptr2->strength = strength;
		vectptr2->priority = 1;
		vectptr2->time = (float)atof(par[i+1]);
		totaltime += vectptr2->time;
		vectptr2->right = 0;
		*vectptr = (char*) vectptr2;
		vectptr = (char**) &(vectptr2->right);
	}
	vectptr2->time = 0.0;

	clokhead = (ROWPTR) simals_alloc_mem((INTBIG)sizeof(ROW));
	if (clokhead == 0) return;
	clokhead->inptr = (IOPTR) vectroot;
	clokhead->outptr = 0;
	clokhead->delta = totaltime;
	clokhead->linear = linear;
	clokhead->exp = 0.0;
	clokhead->abs = 0.0;
	clokhead->random = 0.0;
	clokhead->next = 0;
	clokhead->delay = 0;

	sethead = simals_alloc_link_mem();
	if (sethead == 0) return;
	sethead->type = 'C';
	sethead->ptr = (char *) clokhead;
	sethead->state = num;
	sethead->priority = 1;
	sethead->time = 0.0;
	sethead->right = 0;
	simals_insert_set_list(sethead);

	simals_initialize_simulator(0);
}

/****************************** ERASE ******************************/

void simals_erase_model(void)
{
	MODPTR modptr, nextmodptr;
	EXPTR exptr, nextexptr;
	CONPTR conptr, nextconptr;
	IOPTR ioptr, nextioptr;
	ROWPTR rowptr, nextrowptr;
	LOADPTR loadptr, nextloadptr;
	FUNCPTR funcptr;
	NODEPTR node, nextnode;
	STATPTR statptr, nextstatptr;

	/* reset miscellaneous simulation variables */
	simals_linkfront = 0;
	simals_linkback = 0;

	/* loop through all test vectors */
	simals_clearallvectors(1);

	/* loop throuth all cells in flattened network */
	if (simals_cellroot != 0)
	{
		simals_erase_submod(simals_cellroot);
		efree((char *)simals_cellroot);
		simals_cellroot = 0;
	}
	simals_levelptr = 0;

	/* loop through all nodes in flattened network */
	for(node = simals_noderoot; node != 0; node = nextnode)
	{
		nextnode = node->next;

		/* erase all loads on the nodes */
		for(loadptr = node->pinptr; loadptr != 0; loadptr = nextloadptr)
		{
			nextloadptr = loadptr->next;
			efree((char *)loadptr);
		}

		/* erase all stats on the nodes */
		for(statptr = node->statptr; statptr != 0; statptr = nextstatptr)
		{
			nextstatptr = statptr->next;
			efree((char *)statptr);
		}
		efree((char *)node);
	}
	simals_noderoot = 0;

	/* loop through all primitives in flattened network */
	for(modptr = simals_primroot; modptr != 0; modptr = nextmodptr)
	{
		nextmodptr = modptr->next;
		if (modptr->type == 'F')
		{
			/* loop through each formal port on the function */
			for(exptr = modptr->exptr; exptr != 0; exptr = nextexptr)
			{
				nextexptr = exptr->next;
				efree((char *)exptr);
			}

			/* loop through each parameter on the function */
			funcptr = (FUNCPTR)modptr->ptr;
			for(exptr = funcptr->inptr; exptr != 0; exptr = nextexptr)
			{
				nextexptr = exptr->next;
				efree((char *)exptr);
			}

			efree((char *)funcptr);
		}
		if (modptr->type == 'G')
		{
			/* loop through each row in the gate */
			for(rowptr = (ROWPTR)modptr->ptr; rowptr != 0; rowptr = nextrowptr)
			{
				nextrowptr = rowptr->next;

				/* loop through each input on the row */
				for(ioptr = rowptr->inptr; ioptr != 0; ioptr = nextioptr)
				{
					nextioptr = ioptr->next;
					efree((char *)ioptr);
				}

				/* loop through each output on the row */
				for(ioptr = rowptr->outptr; ioptr != 0; ioptr = nextioptr)
				{
					nextioptr = ioptr->next;
					efree((char *)ioptr);
				}
				if (rowptr->delay != 0) efree((char *)rowptr->delay);
				efree((char *)rowptr);
			}
		}
		if (modptr->level != 0) efree((char *)modptr->level);
		efree((char *)modptr);
	}
	simals_primroot = 0;

	/* loop through each model/gate/function in hierarchical description */
	for(modptr = simals_modroot; modptr != 0; modptr = nextmodptr)
	{
		nextmodptr = modptr->next;
		efree((char *)modptr->name);

		/* loop through each formal port on the model/gate/function */
		for(exptr = modptr->exptr; exptr != 0; exptr = nextexptr)
		{
			nextexptr = exptr->next;
			efree((char *)exptr->node_name);
			efree((char *)exptr);
		}

		/* loop through each "SET" instruction in the model/gate/function */
		for(ioptr = modptr->setptr; ioptr != 0; ioptr = nextioptr)
		{
			nextioptr = ioptr->next;
			efree((char *)ioptr->nodeptr);
			efree((char *)ioptr);
		}

		/* special cleanup for functions */
		if (modptr->type == 'F')
		{
			funcptr = (FUNCPTR)modptr->ptr;

			/* loop through each load in the function */
			for(loadptr = modptr->loadptr; loadptr != 0; loadptr = nextloadptr)
			{
				nextloadptr = loadptr->next;
				efree((char *)loadptr->ptr);
				efree((char *)loadptr);
			}

			/* loop through each input on the function */
			for(exptr = funcptr->inptr; exptr != 0; exptr = nextexptr)
			{
				nextexptr = exptr->next;
				efree((char *)exptr->node_name);
				efree((char *)exptr);
			}
			efree((char *)funcptr);
		}

		/* special cleanup for models */
		if (modptr->type == 'M')
		{
			/* loop through each instance in the model */
			for(conptr = (CONPTR)modptr->ptr; conptr != 0; conptr = nextconptr)
			{
				nextconptr = conptr->next;
				efree((char *)conptr->inst_name);
				if (conptr->model_name != 0) efree(conptr->model_name);

				/* loop through each actual port on the instance */
				for(exptr = conptr->exptr; exptr != 0; exptr = nextexptr)
				{
					nextexptr = exptr->next;
					efree((char *)exptr->node_name);
					efree((char *)exptr);
				}
				efree((char *)conptr);
			}
		}

		/* special cleanup for gates */
		if (modptr->type == 'G')
		{
			/* loop through each row in the gate */
			for(rowptr = (ROWPTR)modptr->ptr; rowptr != 0; rowptr = nextrowptr)
			{
				nextrowptr = rowptr->next;

				/* loop through each input on the row */
				for(ioptr = rowptr->inptr; ioptr != 0; ioptr = nextioptr)
				{
					nextioptr = ioptr->next;
					efree((char *)ioptr->nodeptr);
					efree((char *)ioptr);
				}

				/* loop through each output on the row */
				for(ioptr = rowptr->outptr; ioptr != 0; ioptr = nextioptr)
				{
					nextioptr = ioptr->next;
					efree((char *)ioptr->nodeptr);
					efree((char *)ioptr);
				}
				if (rowptr->delay != 0) efree((char *)rowptr->delay);
				efree((char *)rowptr);
			}

			/* loop through each load in the gate */
			for(loadptr = modptr->loadptr; loadptr != 0; loadptr = nextloadptr)
			{
				nextloadptr = loadptr->next;
				efree((char *)loadptr->ptr);
				efree((char *)loadptr);
			}
		}

		if (modptr->level != 0) efree((char *)modptr->level);
		efree((char *)modptr);
	}
	simals_modroot = 0;
}

/*
 * Routine to clear all test vectors (even the power and ground vectors if "pwrgnd"
 * is nonzero).
 */
void simals_clearallvectors(INTSML pwrgnd)
{
	LINKPTR thisset, vecthead, nextvec, nextset, lastset;
	ROWPTR clokhead;

	lastset = 0;
	for(thisset = simals_setroot; thisset != 0; thisset = nextset)
	{
		nextset = thisset->right;
		if (pwrgnd != 0 || thisset->strength != VDD_STRENGTH)
		{
			if (thisset->type == 'C')
			{
				clokhead = (ROWPTR)thisset->ptr;
				for (vecthead = (LINKPTR)clokhead->inptr; vecthead; vecthead = nextvec)
				{
					nextvec = vecthead->right;
					simals_free_link_mem(vecthead);
				}
				efree((char *)clokhead);
			}
			simals_free_link_mem(thisset);

			if (lastset == 0) simals_setroot = nextset; else
				lastset->right = nextset;
		} else
		{
			lastset = thisset;
		}
	}
}

void simals_erase_submod(CONPTR conhead)
{
	CONPTR conptr, nextconptr;
	EXPTR exptr, nextexptr;
	REGISTER INTBIG chn, i;

	for(conptr = conhead->child; conptr != 0; conptr = nextconptr)
	{
		nextconptr = conptr->next;
		simals_erase_submod(conptr);
		for(exptr = conptr->exptr; exptr != 0; exptr = nextexptr)
		{
			nextexptr = exptr->next;
			efree((char *)exptr);
		}
		efree((char *)conptr);
	}
	if (conhead->display_page != 0)
	{
		chn = simals_levelptr->num_chn + 1;
		for (i = 1; i < chn; i++)
			efree((char *)conhead->display_page[i].name);
		efree((char *)conhead->display_page);
	}
}

/****************************** GO ******************************/

/*
 * Name: simals_go_command
 *
 * Description:
 *	This procedure parses the command line for the go command from the
 * keyboard.  The maximum execution time must also be specified for the
 * simulation run.
 */
void simals_go_command(INTSML count, char *par[])
{
	float max;
	NODEPROTO *np;

	if (sim_window_isactive(&np) == 0)
	{
		ttyputerr(_("No simulator active"));
		return;
	}
	if (count < 1)
	{
		ttyputerr(_("Must specify simulation time"));
		return;
	}
	max = (float)atof(par[0]);
	if (max <= 0.0)
	{
		ttyputerr(_("Simulation time must be greater than 0 seconds"));
		return;
	}
	sim_window_settimerange(0.0, max);

	simals_initialize_simulator(1);
}

/****************************** HELP ******************************/

/*
 * Name: simals_help_command
 *
 * Description:
 *	This procedure process the help command and display help information.
 */
void simals_help_command(void)
{
	(void)us_helpdlog("ALS");
}

/****************************** LEVEL ******************************/

/*
 * Name: simals_level_command
 *
 * Description:
 *	This procedure changes the level of hierarchy that is displayed by the
 * graphics routines.
 */
void simals_level_command(INTSML count, char *par[])
{
	CONPTR  cellptr;
	INTSML   l;
	char    *pp;
	NODEPROTO *np;
	extern COMCOMP sim_alslevelsp;

	if (sim_window_isactive(&np) == 0)
	{
		ttyputerr(_("No simulator active"));
		return;
	}
	if (count < 1)
	{
		ttyputusage("tellaid simulation als level OPTION");
		return;
	}

	l = strlen(pp = par[0]);
	if (namesamen(pp, "up", l) == 0)
	{
		if (simals_levelptr == 0)
		{
			ttyputerr(_("No simulation is running"));
			return;
		}
		cellptr = simals_levelptr->parent;
		if (! cellptr)
		{
			ttyputerr(_("ERROR: Currently at top level of hierarchy"));
			return;
		}
	} else if (namesamen(pp, "set", l) == 0)
	{
		if (count < 2)
		{
			count = ttygetparam(_("Instance to simulate: "), &sim_alslevelsp, 3, &par[1]) + 1;
			if (count < 2) return;
		}
		simals_convert_to_upper(par[1]);
		cellptr = simals_find_level(par[1]);
		if (! cellptr)
		{
			ttyputerr(_("ERROR: Unable to find level %s"), par[1]);
			return;
		}
	} else
	{
		ttyputbadusage("tellaid simulation als level");
		return;
	}

	simals_levelptr = cellptr;
	if (simals_set_current_level() != 0) return;
	l = simals_levelptr->num_chn;
	sim_window_setlines(l);
	simals_initialize_simulator(1);
}

/****************************** PRINT ******************************/

/*
 * routine to print out the display screen status and information
 */
void simals_print_command(INTSML count, char *par[])
{
	INTSML l;
	char *pp, s1[256];
	LINKPTR  linkhead;
	NODEPTR  nodehead;
	STATPTR  stathead;
	MODPTR   primhead;
	ROWPTR   rowhead;
	EXPTR    exhead;
	FUNCPTR  funchead;
	CONPTR   cellhead;

	if (count < 1)
	{
		ttyputusage("tellaid simulation als print OPTION");
		return;
	}
	l = strlen(pp = par[0]);

	if (namesamen(pp, "size", l) == 0 && l >= 2)
	{
		ttyputmsg(_("Number of Primitive Elements in Database = %ld"), simals_pseq);
		ttyputmsg(_("Number of Nodes in Database = %ld"), simals_nseq);
		return;
	}

	if (namesamen(pp, "vector", l) == 0 && l >= 1)
	{
		linkhead = simals_setroot;
		ttyputmsg(_("** VECTOR LINKLIST **"));
		while (linkhead)
		{
			switch (linkhead->type)
			{
				case 'N':
					nodehead = (NODEPTR)linkhead->ptr;
					simals_trans_number_to_state(linkhead->state, s1);
					ttyputmsg(_("***** vector: $N%ld, state = %s, strength = %s, time = %g, priority = %d"),
						nodehead->num, s1, simals_strengthstring(linkhead->strength),
							linkhead->time, linkhead->priority);
					break;
				case 'F':
					stathead = (STATPTR)linkhead->ptr;
					nodehead = stathead->nodeptr;
					simals_trans_number_to_state(linkhead->state, s1);
					ttyputmsg(_("***** function: $N%ld, state = %s, strength = %s, time = %g, priority = %d"),
						nodehead->num, s1, simals_strengthstring(linkhead->strength),
							linkhead->time, linkhead->priority);
					break;
				case 'R':
					ttyputmsg(_("***** rowptr = %ld, time = %g, priority = %d"),
						linkhead->ptr, linkhead->time, linkhead->priority);
					break;
				case 'C':
					ttyputmsg(_("***** clokptr = %ld, time = %g, priority = %d"),
						linkhead->ptr, linkhead->time, linkhead->priority);
			}
			linkhead = linkhead->right;
		}
		return;
	}

	if (namesamen(pp, "netlist", l) == 0 && l >= 1)
	{
		ttyputmsg(_("** NETWORK DESCRIPTION **"));
		for (primhead = simals_primroot; primhead; primhead = primhead->next)
		{
			if (stopping(STOPREASONDISPLAY)) return;
			switch (primhead->type)
			{
				case 'F':
					(void)initinfstr();
					(void)formatinfstr(_("FUNCTION %ld%s: %s("), primhead->num, s1,
						primhead->name);
					for (exhead = primhead->exptr; exhead; exhead=exhead->next)
					{
						if (exhead != primhead->exptr) (void)addstringtoinfstr(", ");
						(void)formatinfstr("N%ld", exhead->nodeptr->num);
					}
					ttyputmsg("%s", returninfstr());
					(void)initinfstr();
					(void)addstringtoinfstr(_("  Event Driving Inputs:"));
					funchead = (FUNCPTR)primhead->ptr;
					for (exhead = funchead->inptr; exhead; exhead=exhead->next)
						(void)formatinfstr(" N%ld", exhead->nodeptr->num);
					ttyputmsg("%s", returninfstr());
					(void)initinfstr();
					(void)addstringtoinfstr(_("  Output Ports:"));
					for (exhead = primhead->exptr; exhead; exhead=exhead->next)
					{
						if (exhead->node_name)
							(void)formatinfstr(" N%ld", ((STATPTR)exhead->node_name)->nodeptr->num);
					}
					ttyputmsg("%s", returninfstr());
					ttyputmsg(_("  Timing: D=%g, L=%g, E=%g, R=%g, A=%g"), funchead->delta,
						funchead->linear, funchead->exp, funchead->random, funchead->abs);
					ttyputmsg(_("  Firing Priority = %d"), primhead->priority);
					break;
				case 'G':
					ttyputmsg(_("GATE %ld: %s  LEVEL: %s"), primhead->num, primhead->name,
						(primhead->level == NULL) ? _("null") : primhead->level);
					for (rowhead = (ROWPTR)primhead->ptr; rowhead; rowhead=rowhead->next)
					{
						ttyputmsg(_("  Timing: D=%g, L=%g, E=%g, R=%g, A=%g"), rowhead->delta,
							rowhead->linear, rowhead->exp, rowhead->random, rowhead->abs);
						ttyputmsg(_("  Delay type: %s"), (rowhead->delay == NULL) ? _("null") : rowhead->delay);
						simals_print_in_entry(rowhead);
						simals_print_out_entry(rowhead);
					}
					ttyputmsg(_("  Firing Priority = %d"), primhead->priority);
					break;
				default:
					ttyputerr(_("Illegal primitive type '%c', database is bad"), primhead->type);
					break;
			}
		}
		return;
	}

	if (namesamen(pp, "xref", l) == 0 && l >= 1)
	{
		ttyputmsg(_("** CROSS REFERENCE TABLE **"));
		simals_print_xref_entry(simals_levelptr, 0);
		return;
	}

	if (namesamen(pp, "state", l) == 0 && l >= 2)
	{
		if (count < 2)
		{
			ttyputusage("tellaid simulation als print state NODENAME");
			return;
		}
		simals_convert_to_upper(par[1]);
		nodehead = simals_find_node(par[1]);
		if (nodehead == 0)
		{
			ttyputerr(_("ERROR: Unable to find node %s"), par[1]);
			return;
		}

		simals_trans_number_to_state(nodehead->new_state, s1);
		ttyputmsg(_("Node %s: State = %s, Strength = %s"), par[1], s1,
			simals_strengthstring(nodehead->new_strength));
		stathead = nodehead->statptr;
		while (stathead)
		{
			simals_trans_number_to_state(stathead->new_state, s1);
			ttyputmsg(_("Primitive %ld:    State = %s, Strength = %s"), stathead->primptr->num,
				s1, simals_strengthstring(stathead->new_strength));
			stathead = stathead->next;
		}
		return;
	}

	if (namesamen(pp, "instances", l) == 0 && l >= 1)
	{
		simals_compute_path_name(simals_levelptr, s1);
		ttyputmsg(_("Instances at level: %s"), s1);
		for (cellhead = simals_levelptr->child; cellhead; cellhead = cellhead->next)
		{
			if (stopping(STOPREASONDISPLAY)) break;
			ttyputmsg(_("Name: %s, Model: %s"), cellhead->inst_name, cellhead->model_name);
		}
		return;
	}
	ttyputbadusage("tellaid simulation als print");
}

char *simals_strengthstring(INTSML strength)
{
	switch (strength)
	{
		case NODE_STRENGTH: return(_("node"));
		case GATE_STRENGTH: return(_("gate"));
		case VDD_STRENGTH:  return(_("power"));
	}
	return(_("off"));
}

/*
 * Name: simals_print_in_entry
 *
 * Description:
 *	This procedure examines an input entry and prints out the condition
 * that it represents.  It is possible for an input entry operand to represent
 * a logic value, integer value, or another node address.
 *
 * Calling Arguments:
 *	rowhead = pointer to the row being printed
 */
void simals_print_in_entry(ROWPTR rowhead)
{
	INTBIG	    num;
	char	   flag, s1[15];
	unsigned char  operatr;
	IOPTR	  iohead;
	NODEPTR	nodehead;

	flag = 0;
	(void)initinfstr();
	(void)addstringtoinfstr(_("  Input: "));

	for (iohead = rowhead->inptr; iohead; iohead = iohead->next)
	{
		if (flag) (void)addstringtoinfstr("& ");
		flag = 1;

		nodehead = (NODEPTR)iohead->nodeptr;
		(void)sprintf(s1, "N%ld", nodehead->num);
		(void)addstringtoinfstr(s1);

		if (iohead->operatr > 127)
		{
			operatr = iohead->operatr - 128;
			nodehead = (NODEPTR) iohead->operand;
			(void)sprintf(s1, "%cN%ld", operatr, nodehead->num);
			(void)addstringtoinfstr(s1);
			continue;
		}

		(void)addtoinfstr(iohead->operatr);

		num = (INTBIG)iohead->operand;
		simals_trans_number_to_state(num, s1);
		(void)addstringtoinfstr(s1);
		(void)addstringtoinfstr(" ");
	}
	ttyputmsg("%s", returninfstr());
}

/*
 * Name: simals_print_out_entry
 *
 * Description:
 *	This procedure examines an output entry and prints out the condition
 * that it represents.  It is possible for an output entry operand to represent
 * a logic value, integer value, or another node address.
 *
 * Calling Arguments:
 *	rowhead = pointer to the row being printed
 */
void simals_print_out_entry(ROWPTR rowhead)
{
	INTBIG	    num;
	char	   flag, s1[50];
	unsigned char  operatr;
	IOPTR	  iohead;
	STATPTR	stathead;
	NODEPTR	nodehead;

	flag = 0;
	(void)initinfstr();
	(void)addstringtoinfstr(_("  Output: "));

	for (iohead = rowhead->outptr; iohead; iohead = iohead->next)
	{
		if (flag) (void)addstringtoinfstr("& ");
		flag = 1;

		stathead = (STATPTR)iohead->nodeptr;
		nodehead = stathead->nodeptr;
		(void)sprintf(s1, "N%ld", nodehead->num);
		(void)addstringtoinfstr(s1);

		if (iohead->operatr > 127)
		{
			operatr = iohead->operatr - 128;
			nodehead = (NODEPTR) iohead->operand;
			(void)sprintf(s1, "%cN%ld@%d ", operatr, nodehead->num, iohead->strength);
			(void)addstringtoinfstr(s1);
			continue;
		}

		(void)addtoinfstr(iohead->operatr);

		num = (INTBIG)iohead->operand;
		simals_trans_number_to_state(num, s1);
		(void)addstringtoinfstr(s1);
		(void)sprintf(s1, "@%d ", iohead->strength);
		(void)addstringtoinfstr(s1);
	}
	ttyputmsg("%s", returninfstr());
}

/*
 * Name: simals_print_xref_entry
 *
 * Description:
 *	This procedure prints entries from the cross reference table that was
 * generated to transform the hierarchical network description into a totally flat
 * network description.  The calling arguments define the root of the reference
 * table column and the level of indentation for the column.
 *
 * Calling Arguments:
 *	cellhead = pointer to cross reference table
 *	tab	= integer value indicating level of output indentation
 */
void simals_print_xref_entry(CONPTR cellhead, INTBIG tab)
{
	INTBIG  i, k, delay;
	char    s1[256], tabsp[200], ts[256];
	EXPTR   exhead;
	CONPTR  subcell;

	if (stopping(STOPREASONDISPLAY)) return;
	simals_compute_path_name(cellhead, s1);
	for (i = 0; i < tab; ++i) tabsp[i] = ' ';
	tabsp[tab] = 0;
	ttyputmsg(_("%sLevel: %s, Model: %s"), tabsp, s1, cellhead->model_name);

	for (exhead = cellhead->exptr; exhead; exhead = exhead->next)
	{
		if (stopping(STOPREASONDISPLAY)) return;
		k = 0;
		for (i=0; i<12; i++)
		{
			delay = exhead->td[i];
			if (delay != 0)
			{
				if (k == 0) (void)initinfstr();
				(void)sprintf(ts, "%s=%ld ", tnames[i], delay);
				(void)addstringtoinfstr(ts);
				k++;
			}
		}
		if (k == 0) ttyputmsg("%s%14s --> N%ld", tabsp, exhead->node_name, exhead->nodeptr->num); else
			ttyputmsg("%s%14s --> N%ld (%s)", tabsp, exhead->node_name, exhead->nodeptr->num, returninfstr());
	}

	if (simals_instbuf[simals_instptr[1]] == 'X') return;

	for (subcell = cellhead->child; subcell; subcell = subcell->next)
		simals_print_xref_entry(subcell, tab + 10);
}

/****************************** SEED ******************************/

/*
 * Name: simals_seed_command
 *
 * Description:
 *	This procedure sets a flag which tells the simulator if it is necessary
 * to reseed the Random Number Generator each time a simulation is run.
 */
void simals_seed_command(INTSML count, char *par[])
{
	if (count < 1)
	{
		ttyputusage("tellaid simulation als seed (reset | no-reset)");
		return;
	}
	if (namesamen(par[0], "reset", strlen(par[0])) == 0)
		simals_seed_flag = 0; else simals_seed_flag = 1;
}

/****************************** SET ******************************/

/*
 * Name: simals_set_command
 *
 * Description:
 *	This procedure sets the specified node to the state that is indicated
 * in the command line.
*/
void simals_set_command(INTSML count, char *par[])
{
	INTBIG	state;
	INTSML	strength;
	float    time;
	NODEPTR  nodehead;
	LINKPTR  sethead;
	NODEPROTO *np;

	if (sim_window_isactive(&np) == 0)
	{
		ttyputerr(_("No simulator active"));
		return;
	}
	if (count < 4)
	{
		ttyputusage("tellaid simulation als set NODE LEVEL STRENGTH TIME");
		return;
	}
	simals_convert_to_upper(par[0]);
	nodehead = simals_find_node(par[0]);
	if (! nodehead)
	{
		ttyputerr(_("ERROR: Unable to find node %s"), par[0]);
		return;
	}

	state = simals_trans_state_to_number(par[1]);
	strength = (INTSML)simals_atoi(par[2]);
	time = (float)atof(par[3]);

	sethead = simals_alloc_link_mem();
	if (sethead == 0) return;
	sethead->type = 'N';
	sethead->ptr = (char *)nodehead;
	sethead->state = state;
	sethead->strength = strength;
	sethead->priority = 2;
	sethead->time = time;
	sethead->right = 0;
	simals_insert_set_list(sethead);

	ttyputmsg(_("Node '%s' scheduled, state = %s, strength = %s, time = %g"), par[0], par[1],
		simals_strengthstring(strength), time);
	simals_initialize_simulator(0);
}

/****************************** TRACE ******************************/

/*
 * Name: simals_trace_command
 *
 * Description:
 *	This procedure turns on/off the trace buffer.  If it is turned off, no
 * timing diagram information will be stored in memory for plotting.
 */
void simals_trace_command(INTSML count, char *par[])
{
	INTSML l;
	char *pp;

	if (count < 1)
	{
		ttyputusage("tellaid simulation als trace OPTION");
		return;
	}
	l = strlen(pp = par[0]);
	if (namesamen(pp, "on", l) == 0 && l >= 2)
	{
		(void)setvalkey((INTBIG)sim_aid, VAID, simals_no_update, 0, VINTEGER);
		return;
	}
	if (namesamen(pp, "off", l) == 0 && l >= 2)
	{
		(void)setvalkey((INTBIG)sim_aid, VAID, simals_no_update, 1, VINTEGER);
		return;
	}
	ttyputbadusage("tellaid simulation als trace");
}

/****************************** VECTOR ******************************/

void simals_vector_command(INTSML count, char *par[])
{
	INTBIG	 i, flag;
	INTSML   l, strength;
	char     s1[256], s2[80], *pp, **vectptr1, **backptr;
	char    *filename, *truename;
	FILE    *vin, *vout;
	LINKPTR  sethead, vecthead, vectptr2, nextvec;
	ROWPTR   clokhead;
	NODEPTR  nodehead;
	float    time;
	NODEPROTO *np;

	if (sim_window_isactive(&np) == 0)
	{
		ttyputerr(_("No simulator active"));
		return;
	}
	if (count < 1)
	{
		ttyputusage("tellaid simulation als vector OPTION");
		return;
	}
	l = strlen(pp = par[0]);

	if (namesamen(pp, "load", l) == 0)
	{
		if (count < 2)
		{
			ttyputusage("tellaid simulation als vector load FILENAME");
			return;
		}
		vin = xopen(par[1], sim_filetypealsvec, "", &filename);
		if (! vin)
		{
			ttyputerr(_("ERROR: Can't open %s"), par[1]);
			return;
		}

		/* clear all vectors */
		while (simals_setroot)
		{
			sethead = simals_setroot;
			simals_setroot = simals_setroot->right;
			if (sethead->type == 'C')
			{
				clokhead = (ROWPTR) sethead->ptr;
				for (vectptr2 = (LINKPTR) clokhead->inptr; vectptr2; vectptr2 = nextvec)
				{
					nextvec = vectptr2->right;
					simals_free_link_mem(vectptr2);
				}
				efree((char *)clokhead);
			}
			simals_free_link_mem(sethead);
		}

		flag = 1;
		while (1)
		{
			if (flag)
			{
				i = xfgets(s1, 255, vin);
				if (i != 0)
				{
					xclose(vin);
					simals_initialize_simulator(0);
					break;
				}
				simals_convert_to_upper(s1);
				if (simals_fragment_command(s1) != 0) break;
			}

			if (! strcmp(simals_instbuf, "CLOCK"))
			{
				simals_convert_to_upper(&(simals_instbuf[simals_instptr[1]]));
				nodehead = simals_find_node(&(simals_instbuf[simals_instptr[1]]));
				if (! nodehead)
				{
					ttyputerr(_("ERROR: Unable to find node %s"),
						&(simals_instbuf[simals_instptr[1]]));
					flag = 1;
					continue;
				}
				strength = atoi(&(simals_instbuf[simals_instptr[9]]));

				sethead = simals_alloc_link_mem();
				if (sethead == 0) return;
				sethead->type = 'C';
				sethead->ptr = (char*)(clokhead = (ROWPTR) simals_alloc_mem((INTBIG)sizeof(ROW)));
				if (sethead->ptr == 0) return;
				sethead->state = atoi(&(simals_instbuf[simals_instptr[13]]));
				sethead->priority = 1;
				sethead->time = (float)atof(&(simals_instbuf[simals_instptr[11]]));
				sethead->right = 0;
				simals_insert_set_list(sethead);

				clokhead->delta = (float)atof(&(simals_instbuf[simals_instptr[3]]));
				clokhead->linear = (float)atof(&(simals_instbuf[simals_instptr[5]]));
				clokhead->exp = (float)atof(&(simals_instbuf[simals_instptr[7]]));
				clokhead->abs = 0.0;
				clokhead->random = 0.0;
				clokhead->next = 0;
				clokhead->delay = 0;

				vectptr1 = (char**) &(clokhead->inptr);
				while (1)
				{
					i = xfgets(s1, 255, vin);
					if (i != 0)
					{
						xclose(vin);
						simals_initialize_simulator(0);
						return;
					}
					simals_convert_to_upper(s1);
					if (simals_fragment_command(s1) != 0) return;
					if (!strcmp(simals_instbuf, "CLOCK") || !strcmp(simals_instbuf, "SET"))
					{
						flag = 0;
						break;
					}
					vectptr2 = simals_alloc_link_mem();
					if (vectptr2 == 0) return;
					vectptr2->type = 'N';
					vectptr2->ptr = (char *)nodehead;
					vectptr2->state = simals_trans_state_to_number(simals_instbuf);
					vectptr2->strength = strength;
					vectptr2->priority = 1;
					vectptr2->time = (float)atof(&(simals_instbuf[simals_instptr[1]]));
					vectptr2->right = 0;
					*vectptr1 = (char*) vectptr2;
					vectptr1 = (char**) &(vectptr2->right);
				}
			}

			if (! strcmp(simals_instbuf, "SET"))
			{
				simals_convert_to_upper(&(simals_instbuf[simals_instptr[1]]));
				nodehead = simals_find_node(&(simals_instbuf[simals_instptr[1]]));
				if (! nodehead)
				{
					ttyputerr(_("ERROR: Unable to find node %s"),
						&(simals_instbuf[simals_instptr[1]]));
					flag = 1;
					continue;
				}

				sethead = simals_alloc_link_mem();
				if (sethead == 0) return;
				sethead->type = 'N';
				sethead->ptr = (char *) nodehead;
				sethead->state = simals_trans_state_to_number(&(simals_instbuf[simals_instptr[2]]));
				sethead->strength = atoi(&(simals_instbuf[simals_instptr[3]]));
				sethead->priority = 2;
				sethead->time = (float)atof(&(simals_instbuf[simals_instptr[5]]));
				sethead->right = 0;
				simals_insert_set_list(sethead);
				flag = 1;
			}
		}
		return;
	}

	if (namesamen(pp, "new", l) == 0)
	{
		/* clear all vectors */
		simals_clearallvectors(0);
		simals_initialize_simulator(0);
		return;
	}

	if (namesamen(pp, "save", l) == 0)
	{
		if (count < 2)
		{
			ttyputusage("tellaid simulation als vector save FILENAME");
			return;
		}
		vout = xcreate(par[1], sim_filetypealsvec, _("Vector File"), &truename);
		if (vout == 0)
		{
			if (truename != 0) ttyputerr(_("ERROR: Can't create %s"), truename);
			return;
		}

		for (sethead = simals_setroot; sethead; sethead = sethead->right)
		{
			switch (sethead->type)
			{
				case 'C':
					clokhead = (ROWPTR)sethead->ptr;
					vecthead = (LINKPTR)clokhead->inptr;
					simals_compute_node_name((NODEPTR)vecthead->ptr, s1);
					xprintf(vout, "CLOCK %s D=%g L=%g E=%g ", s1, clokhead->delta,
						clokhead->linear, clokhead->exp);
					xprintf(vout, "STRENGTH=%d TIME=%g CYCLES=%ld\n", vecthead->strength,
						sethead->time, sethead->state);
					for (; vecthead; vecthead = vecthead->right)
					{
						simals_trans_number_to_state(vecthead->state, s2);
						xprintf(vout, "  %s %g\n", s2, vecthead->time);
					}
					break;
				case 'N':
					simals_compute_node_name((NODEPTR)sethead->ptr, s1);
					simals_trans_number_to_state(sethead->state, s2);
					xprintf(vout, "SET %s=%s@%d TIME=%g\n", s1, s2, sethead->strength,
						sethead->time);
			}
		}
		xclose(vout);
		return;
	}

	if (namesamen(pp, "delete", l) == 0)
	{
		if (count < 3)
		{
			ttyputusage("tellaid simulation als vector delete NODE OPTIONS");
			return;
		}
		simals_convert_to_upper(par[1]);
		nodehead = simals_find_node(par[1]);
		if (! nodehead)
		{
			ttyputerr(_("ERROR: Unable to find node %s"), par[1]);
			return;
		}

		backptr = (char**) &simals_setroot;
		sethead = simals_setroot;

		if (par[2][0] == 'a')
		{
			while (sethead)
			{
				if (sethead->type == 'C')
				{
					clokhead = (ROWPTR)sethead->ptr;
					vecthead = (LINKPTR)clokhead->inptr;
					if ((NODEPTR)vecthead->ptr == nodehead)
					{
						*backptr = (char *)sethead->right;
						simals_free_link_mem(sethead);
						sethead = (LINKPTR)*backptr;
						efree((char *)clokhead);
						for (; vecthead; vecthead = nextvec)
						{
							nextvec = vecthead->right;
							simals_free_link_mem(vecthead);
						}
						continue;
					}
				} else
				{
					if ((NODEPTR)sethead->ptr == nodehead)
					{
						*backptr = (char *)sethead->right;
						simals_free_link_mem(sethead);
						sethead = (LINKPTR)*backptr;
						continue;
					}
				}

				backptr = (char**) &(sethead->right);
				sethead = sethead->right;
			}
			simals_initialize_simulator(0);
			return;
		}

		if (count < 4)
		{
			ttyputusage("tellaid simulation als vector delete time TIME");
			return;
		}
		time = (float)atof(par[2]);
		while (sethead)
		{
			if (sethead->time == time)
			{
				if (sethead->type == 'C')
				{
					clokhead = (ROWPTR)sethead->ptr;
					vecthead = (LINKPTR)clokhead->inptr;
					if ((NODEPTR)vecthead->ptr == nodehead)
					{
						*backptr = (char*)sethead->right;
						simals_free_link_mem(sethead);
						sethead = (LINKPTR)*backptr;
						efree((char *)clokhead);
						for (; vecthead; vecthead = nextvec)
						{
							nextvec = vecthead->right;
							simals_free_link_mem(vecthead);
						}
						simals_initialize_simulator(0);
						return;
					}
				} else
				{
					if ((NODEPTR)sethead->ptr == nodehead)
					{
						*backptr = (char *)sethead->right;
						simals_free_link_mem(sethead);
						sethead = (LINKPTR)*backptr;
						simals_initialize_simulator(0);
						return;
					}
				}
			}

			backptr = (char**) &(sethead->right);
			sethead = sethead->right;
		}
		return;
	}

	ttyputbadusage("tellaid simulation als vector");
}

/****************************** ANNOTATE ******************************/

/*
 * Name: simals_annotate_command
 *
 * Description:
 *   Annotate node information onto corresponding schematic.
 */
void simals_annotate_command(INTSML count, char *par[])
{
	if (count < 1)
	{
		ttyputusage("tellaid simulation als annotate [minimum | typical | maximum]");
		return;
	}

	if (simals_levelptr == 0)
	{
		ttyputerr(_("Must start simulator before annotating delay information"));
		return;
	}

	if (namesamen(par[0], "min", 3) == 0) simals_sdfdelaytype = DELAY_MIN;
		else if (namesamen(par[0], "typ", 3) == 0) simals_sdfdelaytype = DELAY_TYP;
			else if (namesamen(par[0], "max", 3) == 0) simals_sdfdelaytype = DELAY_MAX;
				else
	{
		ttyputbadusage("tellaid simulation als annotate");
		return;
	}

	simals_sdfannotate(simals_levelptr);
	simals_update_netlist();
	ttyputmsg(_("Completed annotation of SDF %s delay values"), par[0]);
}

/*
 * Routine to annotate SDF port delay info onto ALS netlist.
 */
void simals_sdfannotate(CONPTR cellhead)
{
	char    s1[256];
	CONPTR  subcell;
	NODEINST *ni;

	if (stopping(STOPREASONDISPLAY)) return;
	simals_compute_path_name(cellhead, s1);

	ni = simals_getcellinstance(cellhead->model_name, s1);
	if (ni != NONODEINST)
	{
		simals_sdfportdelay(cellhead, ni, s1);
	}

	if (simals_instbuf[simals_instptr[1]] == 'X') return;

	for (subcell = cellhead->child; subcell; subcell = subcell->next)
		simals_sdfannotate(subcell);
}

/*
 * Routine to get a NODEINST for specified cell instance.
 */
NODEINST *simals_getcellinstance(char *celltype, char *instance)
{
	NODEINST *ni;
	NODEPROTO *np;
	VARIABLE *var;
	char *pt, **instlist, *str, tmp[256];
	INTSML i, j, count = 1;

	np = getcurfacet();
	ni = NONODEINST;

	/* count number of hierarchy levels */
	(void)sprintf(tmp, "%s", instance);
	for (pt = tmp; *pt != 0; pt++) if (*pt == '.') count++;

	/* separate out each hiearchy level - skip first level which is the top */
	instlist = (char **)emalloc(count * (sizeof(char *)), el_tempcluster);
	pt = instance;
	for (i=0, j=0; i<count; i++)
	{
		str = getkeyword(&pt, ".");
		if (i >= 2) if (allocstring(&instlist[j++], str, el_tempcluster) != 0)
			return(NONODEINST);
		(void)tonextchar(&pt);
	}
	count -= 2;

	if (count == 0) return(NONODEINST);

	/* find the NODEINST corresponding to bottom level of hierarchy */
	for(i=0; i<count; i++)
	{
		for (ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
			if (var == NOVARIABLE) continue;
			if (namesame((char *)var->addr, instlist[i]) != 0) continue;
			np = ni->proto;
			break;
		}
		if (ni == NONODEINST) break;
		if (np->primindex != 0) break;
	}

	return(ni);
}

/*
 * Routine to extract SDF port delay information and annotate it to ALS netlist.
 */
void simals_sdfportdelay(CONPTR cellhead, NODEINST *ni, char *path)
{
	VARIABLE *var;
	PORTARCINST *pi;
	INTBIG len, i, j, delay;
	EXPTR exhead;

	for (exhead = cellhead->exptr; exhead; exhead = exhead->next)
	{
		for (pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			if (namesame(pi->proto->protoname, exhead->node_name) == 0)
			{
				var = getval((INTBIG)pi, VPORTARCINST, VSTRING|VISARRAY, "SDF_absolute_port_delay");
				if (var != NOVARIABLE)
				{
					len = getlength(var);
					for (i=0; i<len; i++)
					{
						if (namesamen(path, ((char **)var->addr)[i], strlen(path)) == 0)
						{
							for (j=0; j<12; j++)
							{
								delay = simals_getportdelayvalue(((char **)var->addr)[i],
									tnames[j], simals_sdfdelaytype);
								if (delay != -1) exhead->td[j] = delay; else
									exhead->td[j] = 0;
							}
						}
					}
				}
			}
		}
	}
}

/*
 * Routine to extract delay value from delay data string.
 */
INTBIG simals_getportdelayvalue(char *datastring, char *transition, DELAY_TYPES delaytype)
{
	char *pt, *ts, **instlist, *str, tmp[256];
	INTSML i, count = 1;

	/* count number of parts in data string */
	(void)sprintf(tmp, "%s", datastring);
	for (pt = tmp; *pt != 0; pt++) if (*pt == ' ') count++;

	/* split data string into separate pieces */
	instlist = (char **)emalloc(count * (sizeof(char *)), el_tempcluster);
	pt = datastring;
	for (i=0; i<count; i++)
	{
		str = getkeyword(&pt, " ");
		if (allocstring(&instlist[i], str, el_tempcluster) != 0) return(-1);
	}

	/* get piece that corresponds to specified transition */
	for (i=0; i<count; i++)
	{
		if (namesamen(instlist[i], transition, strlen(transition)) == 0)
		{
			if (allocstring(&ts, instlist[i], el_tempcluster) != 0) return(-1);
			return(simals_getdval(ts, delaytype));
		}
	}

	return(-1);
}

/*
 * Routine to get a delay value string from a transition string.
 *     if tstring is '01(111:222:333)' and delaytype is DELAY_TYP return 222
 */
INTBIG simals_getdval(char *tstring, DELAY_TYPES delaytype)
{
	char *pt, *str, *bs, ts[256], *t1, *t2, *t3;
	INTSML i, start = 0, stop = 0;

	bs = str = (char *)emalloc(strlen(tstring), el_tempcluster);
	for (pt = tstring; *pt != 0; pt++)
	{
		if (*pt == ')') stop++;
		if (start && !stop) *str++ = *pt;
		if (*pt == '(') start++;
	}
	*str = 0;

	(void)sprintf(ts, "%s", bs);

	/* delay string is not a triple, only one delay value implies typical */
	if (strstr(ts, ":") == NULL)
	{
		if (delaytype == DELAY_TYP) return(atoi(ts));
		return(-1);
	}

	pt = ts;
	for (i=0; i<3; i++)
	{
		str = getkeyword(&pt, ":");
		if (i == 0) if (allocstring(&t1, str, el_tempcluster) != 0) return(-1);
		if (i == 1) if (allocstring(&t2, str, el_tempcluster) != 0) return(-1);
		if (i == 2) if (allocstring(&t3, str, el_tempcluster) != 0) return(-1);
		(void)tonextchar(&pt);
	}

	switch (delaytype)
	{
		case DELAY_MIN:
			return(atoi(t1));
		case DELAY_TYP:
			return(atoi(t2));
		case DELAY_MAX:
			return(atoi(t3));
		default:
			return(-1);
	}
}

/****************************** ORDER ******************************/

/*
 * Name: simals_order_command
 *
 * Description:
 *   Save/restore signal trace order for waveform display.
 */
void simals_order_command(INTSML count, char *par[])
{
	if (count < 1)
	{
		ttyputusage("tellaid simulation als order [save | restore]");
		return;
	}

	if (namesamen(par[0], "sav", 3) == 0) simals_order_save();
		else if (namesamen(par[0], "res", 3) == 0)
	{
		if (count != 2)
		{
			ttyputusage("tellaid simulation als order restore OPTION");
			return;
		}
		simals_order_restore(par[1]);
	} else
	{
		ttyputbadusage("tellaid simulation als order");
		return;
	}
}

void simals_order_save(void)
{
	INTBIG tr;
	INTSML first = 0;
	char str[256], *ts;
	NODEPROTO *curfacet;

	sim_window_inittraceloop();
	while ((tr = sim_window_nexttraceloop()) != 0)
	{
		if (!first) (void)initinfstr();
		(void)sprintf(str, "%s:", sim_window_gettracename(tr));
		(void)addstringtoinfstr(str);
		first++;
	}

	if (first)
	{
		ts = returninfstr();
		ts[(strlen(ts)-1)] = 0;  /* chop off trailing ":" */

		/* save on current facet */
		curfacet = getcurfacet();
		if (curfacet != NULL) (void)setval((INTBIG)curfacet, VNODEPROTO,
			"SIM_als_trace_order", (INTBIG)ts, VSTRING);
	}
}

void simals_order_restore(char *list)
{
	INTSML tc = 0, i, found, lines, thispos, pos, fromlib = 0;
	INTBIG tr, trl;
	NODEPTR node;
	VARIABLE *var;
	NODEPROTO *curfacet;
	char *pt, *str, **tl, tmp[256];

	if (namesame(list, "fromlib") == 0)
	{
		curfacet = getcurfacet();
		if (curfacet != NONODEPROTO)
		{
			var = getval((INTBIG)curfacet, VNODEPROTO, VSTRING, "SIM_als_trace_order");
			if (var != NOVARIABLE)
			{
				(void)sprintf(tmp, "%s", (char *)var->addr);
				fromlib++;
			}
			else return;
		}
		else return;
	}
	else (void)sprintf(tmp, "%s", list);

	/* count number of traces and fill trace list array */
	for (pt = tmp; *pt != 0; pt++) if (*pt == ':') tc++;
	if (tc == 0) return;
	tc++;
	tl = (char **)emalloc(tc * (sizeof(char *)), el_tempcluster);
	pt = tmp;
	for (i=0; i<tc; i++)
	{
		str = getkeyword(&pt, ":");
		(void)allocstring(&tl[i], str, el_tempcluster);
		(void)tonextchar(&pt);
	}

	/* delete traces not in restored list */
	sim_window_sethighlighttrace(0);
	sim_window_inittraceloop();
	while ((tr = sim_window_nexttraceloop()) != 0)
	{
		found = 0;
		for (i=0; i<tc; i++)
		{
			if (namesame(sim_window_gettracename(tr), tl[i]) == 0) found++;
		}
		if (!found)
		{
			thispos = sim_window_gettraceline(tr);
			sim_window_inittraceloop2();
			for(;;)
			{
				trl = sim_window_nexttraceloop2();
				if (trl == 0) break;
				pos = sim_window_gettraceline(trl);
				if (pos > thispos) sim_window_settraceline(trl, (INTSML)(pos-1));
			}
			lines = sim_window_getlines();
			if (lines > 1) sim_window_setlines((INTSML)(lines-1));

			/* remove from the simulator's list */
			for(i=0; i<simals_levelptr->num_chn; i++)
			{
				node = simals_levelptr->display_page[i+1].nodeptr;
				if (node == 0) continue;
				if (simals_levelptr->display_page[i+1].displayptr == tr)
				{
					simals_levelptr->display_page[i+1].displayptr = 0;
					break;
				}
			}

			/* kill trace, redraw */
			sim_window_killtrace(tr);
		}
	}

	/* order the traces */
	sim_window_setlines(tc);
	sim_window_inittraceloop();
	while ((tr = sim_window_nexttraceloop()) != 0)
	{
		for (i=0; i<tc; i++)
		   if (namesame(tl[i], sim_window_gettracename(tr)) == 0) break;
		if (fromlib) sim_window_settraceline(tr, (INTSML)(tc-i-1)); else  /* order from library save is bottom to top */
			sim_window_settraceline(tr, i);
	}

	sim_window_redraw();
}

/*
 * Update the flattened netlist with the annotated delay values.
 */
void simals_update_netlist(void)
{
	MODPTR primhead;
	CONPTR cellhead;
	ROWPTR rowhead;
	EXPTR exhead;
	INTBIG delay, max_delay;

	for (primhead = simals_primroot; primhead; primhead = primhead->next)
	{
		switch(primhead->type)
		{
			case 'F':
				break;

			case 'G':
				/* cycle through all entries in table */
				for (rowhead = (ROWPTR)primhead->ptr; rowhead; rowhead=rowhead->next)
				{
					/* check for valid delay transition name for current entry */
					if (strcmp(rowhead->delay, "XX"))
					{
						/* TESTING - get the max delay value of all input ports matching transition */
						cellhead = simals_find_level(simals_parent_level(primhead->level));
						max_delay = 0;
						for (exhead = cellhead->exptr; exhead; exhead = exhead->next)
						{
							delay = exhead->td[simals_get_tdindex(rowhead->delay)];
							if (max_delay < delay) max_delay = delay;
						}
						if (max_delay != 0)
						{
							rowhead->abs = (float)max_delay * 1.0e-12f;
						}
						ttyputmsg(_("*** DEBUG *** gate: %s, level: %s, delay: %g(%s)"),
							primhead->name, primhead->level, (float)max_delay * 1.0e-12, rowhead->delay);
						ttyputmsg(_("  Timing: D=%g, L=%g, E=%g, R=%g, A=%g"),
							rowhead->delta, rowhead->linear, rowhead->exp, rowhead->random, rowhead->abs);
						simals_print_in_entry(rowhead);
						simals_print_out_entry(rowhead);
					}
				}
				break;

			default:
				ttyputerr(_("Illegal primitive type '%c', database is bad"), primhead->type);
				break;
		}
	}
}

/*
 * Return index for transition delays given text name.
 */
INTSML simals_get_tdindex(char *name)
{
	INTSML i;

	for (i=0; i<12; i++)
	{
		if (!strcmp(tnames[i], name)) return(i);
	}
	return(0);  /* return '01' index */
}

/*
 * Return the parent level of the given child.
 *     if .TOP.NODE3.G1 is child, .TOP.NODE3 is parent
 */
char *simals_parent_level(char *child)
{
	char tmp[256], *pt, *str, **instlist;
	INTSML i, count = 1;

	(void)sprintf(tmp, "%s", child);
	for (pt = tmp; *pt != 0; pt++) if (*pt == '.') count++;

	/* separate out each hiearchy level */
	instlist = (char **)emalloc(count * (sizeof(char *)), el_tempcluster);
	pt = child;
	for (i=0; i<count; i++)
	{
		str = getkeyword(&pt, ".");
		(void)allocstring(&instlist[i], str, el_tempcluster);
		(void)tonextchar(&pt);
	}

	/* create the parent level name */
	(void)initinfstr();
	for (i=0; i<count-1; i++)
	{
		(void)addstringtoinfstr(instlist[i]);
		if (i != (count - 2)) (void)addstringtoinfstr(".");
	}

	return(returninfstr());
}

#endif  /* SIMAID - at top */
