/*
 * Electric(tm) VLSI Design System
 *
 * File: simwindow.c
 * Simulation tool: signal window control
 * Written by: Steven M. Rubin, Static Free Software
 *
 * 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 SIMTOOL

#include "global.h"
#include "egraphics.h"
#include "edialogs.h"
#include "sim.h"
#include "simals.h"
#include "tecgen.h"
#include "tecschem.h"
#include "tecart.h"
#include "usr.h"
#include "usrtrack.h"

/*
 *     WINDOW CONTROL:
 * sim_window_init()                            Initialize simulation window system (do only once)
 * sim_window_term()                            Terminate simulation window system (do only once)
 * BOOLEAN sim_window_create(l, np, chw, chs, f) Make simulation window with "l" lines, format "f"
 *                                              in facet "np" charhandlers "chw/s" (l=0 to restore)
 * void sim_window_stopsimulation()             Stops simulation
 * INTBIG sim_window_isactive(np)               Returns nonzero if simulating, returns facet if so
 * sim_window_redraw()                          Redraws simulation window
 * sim_window_setlines(count)                   Sets number of lines in simulation window
 * INTBIG sim_window_getlines()                 Returns number of lines in sim window
 * INTBIG sim_window_gettopline()               Returns top line in simulation window
 * sim_window_settopline(count)                 Sets top visible line in simulation window
 * INTBIG sim_window_getvislines()              Returns number of visible lines in sim window
 * sim_window_savegraph()                       Preserves plot in database
 * sim_window_displaycolor(s, c)                Set color of strength/state "s" to "c"
 *
 *     CONTROL OF TRACES:
 * INTBIG sim_window_newtrace(line, name, data) Creates trace at "line" called "name" with user "data"
 * BOOLEAN sim_window_buscommand()              Creates a bus from currently selected signals
 * sim_window_loaddigtrace(tr, num, time, sta)  Loads trace "tr" with "num" digital signals "time/sta"
 * sim_window_loadanatrace(tr, num, time, val)  Loads trace "tr" with "num" analog signals "time/val"
 * sim_window_setanarange(low, high)            Sets analog display range from "low" to "high"
 * sim_window_getanarange(low, high)            Gets analog display range
 * sim_window_setanaextents(low, high)          Sets fixed analog extent from "low" to "high"
 * sim_window_getanaextents(low, high)          Gets fixed analog extent
 * sim_window_settraceline(tr, line)            Sets line number for trace "tr"
 * sim_window_killtrace(tr)                     Deletes trace "tr"
 * sim_window_killalltraces(save)               Deletes all traces
 *
 *     TRACE INFORMATION:
 * INTBIG *sim_window_getbustraces(tr)          Returns 0-terminated list of traces in bus "tr"
 * INTBIG sim_window_gettraceline(tr)           Returns line number for trace "tr"
 * char *sim_window_gettracename(tr)            Returns name for trace "tr"
 * INTBIG sim_window_gettracedata(tr)           Returns user data for trace "tr"
 * float sim_window_getanatracevalue(tr, time)  Returns analog value for trace "tr" at time "time"
 * INTBIG sim_window_findtrace(name)            Locates the trace with this name
 * sim_window_inittraceloop()                   Begin search of all traces
 * sim_window_inittraceloop2()                  Begin search of all traces
 * INTBIG sim_window_nexttraceloop()            Returns next trace (0 when done)
 * INTBIG sim_window_nexttraceloop2()           Returns next trace (0 when done)
 *
 *     TRACE HIGHLIGHTING:
 * sim_window_cleartracehighlight()             Unhighlights all traces
 * sim_window_addhighlighttrace(tr)             Adds trace "tr" to highlighting
 * sim_window_deletehighlighttrace(tr)          Removes trace "tr" from highlighting
 * INTBIG sim_window_gethighlighttrace()        Returns highlighted trace (0 if none)
 * INTBIG *sim_window_gethighlighttraces()      Returns 0-terminated list of highlighted traces
 *
 *     TIME AND CURSOR CONTROL:
 * sim_window_setmaincursor(time)               Sets position of main cursor
 * double sim_window_getmaincursor()            Returns position of main cursor
 * sim_window_setextensioncursor(time)          Sets position of extension cursor
 * double sim_window_getextensioncursor()       Returns position of extension cursor
 * sim_window_settimerange(min, max)            Sets window time range from "min" to "max"
 * sim_window_gettimerange(min, max)            Returns window time range
 * sim_window_gettimeextents(min, max)          Returns data time range
 */

#define BUSANGLE          4		/* defines look of busses */

#define NODISCHANNEL ((DISCHANNEL *)-1)

/* the meaning of DISCHANNEL->flags */
#define TRACEWANTSELECT  01		/* set if this trace wants to be selected */
#define TRACETYPE        06		/* the type of this trace */
#define TRACEISDIGITAL    0		/*     this trace is a digital signal */
#define TRACEISANALOG    02		/*     this trace is an analog signal */
#define TRACEISBUS       04		/*     this trace is a bus */
#define TRACEISOPENBUS   06		/*     this trace is an opened bus */
#define TRACENOTDRAWN   010		/* set if trace is not to be drawn */

typedef struct Idischannel
{
	char     *name;				/* name of this trace */
	char     *origname;			/* original name of this trace */
	INTBIG    flags;			/* state bits for this trace (see above) */
	INTBIG    nodeptr;			/* "user" data for this trace */
	INTBIG    busindex;			/* index of this trace in the bus (when part of a bus) */
	INTBIG    color;			/* color of this trace (analog only) */
	INTBIG    nameoff;			/* offset of trace name (when many per line) */
	INTBIG    position;			/* line number of this trace in window */
	INTBIG    numsteps;			/* number of steps along this trace */
	INTBIG    timelimit;		/* maximum times allocated for this trace */
	INTBIG    statelimit;		/* maximum states allocated for this trace */
	INTBIG    valuelimit;		/* maximum values allocated for this trace */
	double   *timearray;		/* time steps along this trace */
	INTBIG   *statearray;		/* states along this trace (digital) */
	float    *valuearray;		/* values along this trace (analog) */
	struct Idischannel *buschannel;
	struct Idischannel *nextdischannel;
} DISCHANNEL;

static GRAPHICS sim_window_desc = {LAYERA, ALLOFF, SOLIDC, SOLIDC,
	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};
static INTBIG sim_window_anacolor[] = {RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW, GRAY, ORANGE,
	PURPLE, BROWN, LGRAY, DGRAY, LRED, DRED, LGREEN, DGREEN, LBLUE, DBLUE, 0};

/*
 * In X:
 *    0 to 112 is signal name
 *      113    is bar
 *   114-767 is signal waveform
 * In Y:
 *    0 to  30 is axis label
 *   31 to 570 is signal waveforms
 *  571 to 600 is cursor value
 */
static INTBIG        sim_window_curx, sim_window_cury;
static INTBIG        sim_window_textsize;
static INTBIG        sim_window_lines;
static INTBIG        sim_window_vislines;
static INTBIG        sim_window_topline;
static INTBIG        sim_window_txthei;
static INTBIG        sim_window_offx, sim_window_offy;
static INTBIG        sim_window_basex, sim_window_basey;
       INTBIG        sim_window_statekey;		/* key for "SIM_window_state" */
       INTBIG        sim_window_state;			/* cached value of "SIM_window_state" */
       INTBIG        sim_window_format;			/* type of simulation in window */
       INTBIG        sim_window_signalorder_key;/* key for "SIM_window_signalorder" */
static double        sim_window_maincursor, sim_window_extensioncursor;
static double        sim_window_mintime, sim_window_maxtime;
static float         sim_window_analow, sim_window_anahigh, sim_window_anarange;
static float         sim_window_anadislow, sim_window_anadishigh, sim_window_anadisrange;
static INTBIG        sim_window_highlightedtracecount;
static INTBIG        sim_window_highlightedtracetotal = 0;
static DISCHANNEL  **sim_window_highlightedtraces;
static INTBIG        sim_window_bustracetotal = 0;
static DISCHANNEL  **sim_window_bustraces;
static DISCHANNEL   *sim_window_plot;
static DISCHANNEL   *sim_window_plotfree;
static DISCHANNEL   *sim_window_loop;
static DISCHANNEL   *sim_window_loop2;
static INTBIG        sim_colorstrengthoff;		/* color of off-strength */
static INTBIG        sim_colorstrengthnode;		/* color of node-strength */
static INTBIG        sim_colorstrengthgate;		/* color of gate-strength */
static INTBIG        sim_colorstrengthpower;	/* color of power-strength */
static INTBIG        sim_colorlevellow;			/* color of low-levels */
static INTBIG        sim_colorlevelhigh;		/* color of high-levels */
static INTBIG        sim_colorlevelundef;		/* color of power-levels */
static WINDOWPART   *sim_waveformwindow;		/* current window when arrows are clicked */
static INTBIG        sim_waveformsliderpart;	/* 0:down arrow 1:below thumb 2:thumb 3:above thumb 4:up arrow */
static INTBIG        sim_window_tracedragyoff;	/* Y offset when dragging */
static INTBIG        sim_window_initialx;		/* initial X coordinate when dragging */
static INTBIG        sim_window_initialy;		/* initial Y coordinate when dragging */
static INTBIG        sim_window_currentdragy;	/* current Y coordinate when dragging */
static INTBIG        sim_window_deltasofar;		/* for dragging thumb button on side */
static INTBIG        sim_window_initialthumb;	/* for dragging thumb button on side */
static DISCHANNEL   *sim_window_tracedragtr;	/* signal being dragged */
static INTBIG        sim_window_sigtotal = 0;
static INTBIG       *sim_window_sigvalues;

/* prototypes for local routines */
static void        sim_window_addsegment(WINDOWPART*, INTBIG*, INTBIG*, INTBIG, INTBIG, INTBIG, INTBIG);
static void        sim_window_addtrace(DISCHANNEL *tr, DISCHANNEL *aftertr);
static DISCHANNEL *sim_window_allocdischannel(void);
static void        sim_window_buttonhandler(WINDOWPART*, INTBIG, INTBIG, INTBIG);
static void        sim_window_drawbox(WINDOWPART*, INTBIG, INTBIG, INTBIG, INTBIG);
static void        sim_window_drawcstring(WINDOWPART*, char*);
static void        sim_window_drawcursors(WINDOWPART*);
static void        sim_window_drawgraph(WINDOWPART*);
static void        sim_window_drawhcstring(WINDOWPART *w, char *s);
static void        sim_window_drawrstring(WINDOWPART*, char*);
static void        sim_window_drawtimescale(WINDOWPART*);
static void        sim_window_drawto(WINDOWPART*, INTBIG, INTBIG);
static void        sim_window_drawtracedrag(WINDOWPART*, BOOLEAN on);
static void        sim_window_drawtraceselect(WINDOWPART *wavewin, INTBIG y1, INTBIG y2, BOOLEAN on);
static void        sim_window_drawulstring(WINDOWPART *w, char *s);
static BOOLEAN     sim_window_eachbdown(INTBIG, INTBIG);
static BOOLEAN     sim_window_eachdown(INTBIG x, INTBIG y);
static BOOLEAN     sim_window_eachwdown(INTBIG, INTBIG);
static WINDOWPART *sim_window_findschematics(void);
static WINDOWPART *sim_window_findwaveform(void);
static void        sim_window_getproperunit(double time, char **sectype, INTBIG *inttime);
static BOOLEAN     sim_window_horizontalslider(INTBIG x, INTBIG y);
static void        sim_window_hthumbtrackingcallback(INTBIG delta);
static char       *sim_window_makewidevalue(INTBIG count, INTBIG *value);
static void        sim_window_mapcoord(WINDOWPART*, INTBIG*, INTBIG*);
static void        sim_window_moveto(INTBIG, INTBIG);
static char       *sim_window_namesignals(INTBIG count, DISCHANNEL **list);
static void        sim_window_plottrace(DISCHANNEL *tr, WINDOWPART *wavewin, INTBIG highlight);
static void        sim_window_redisphandler(WINDOWPART*);
static void        sim_window_removetrace(DISCHANNEL *tr);
static void        sim_window_renumberlines(void);
static void        sim_window_savesignalorder(void);
static void        sim_window_scaletowindow(WINDOWPART*, INTBIG*, INTBIG*);
static BOOLEAN     sim_window_seleachdown(INTBIG x, INTBIG y);
static double      sim_window_sensibletimevalue(double time);
static void        sim_window_setcolor(INTBIG);
static void        sim_window_setmask(INTBIG);
static void        sim_window_setviewport(WINDOWPART*);
static INTBIG      sim_window_timetoxpos(double);
static BOOLEAN     sim_window_verticalslider(INTBIG x, INTBIG y);
static void        sim_window_vthumbtrackingcallback(INTBIG delta);
static void        sim_window_writetracename(WINDOWPART*, DISCHANNEL*);
static double      sim_window_xpostotime(INTBIG);

/********************************* WINDOW CONTROL *********************************/

/*
 * routine to initialize the simulation window
 */
void sim_window_init(void)
{
	sim_window_signalorder_key = makekey("SIM_window_signalorder");

	sim_window_statekey = makekey("SIM_window_state");
#if SIMTOOLIRSIM == 0
	sim_window_state = FULLSTATE | SHOWWAVEFORM;
#else
	sim_window_state = FULLSTATE | SHOWWAVEFORM | SIMENGINEIRSIM;
#endif
	(void)setvalkey((INTBIG)sim_tool, VTOOL, sim_window_statekey,
		sim_window_state, VINTEGER|VDONTSAVE);

	sim_window_mintime = 0.0;
	sim_window_maxtime = 0.0000005f;
	sim_window_maincursor = 0.0000002f;
	sim_window_extensioncursor = 0.0000003f;
	sim_window_lines = 0;
	sim_window_vislines = 0;
	sim_window_topline = 0;
	sim_window_plot = NODISCHANNEL;
	sim_window_plotfree = NODISCHANNEL;
	sim_window_highlightedtracecount = 0;
	sim_colorstrengthoff = BLUE;
	sim_colorstrengthnode = GREEN;
	sim_colorstrengthgate = MAGENTA;
	sim_colorstrengthpower = BLACK;
	sim_colorlevellow = BLUE;
	sim_colorlevelhigh = MAGENTA;
	sim_colorlevelundef = BLACK;
}

/*
 * routine to terminate the simulation window
 */
void sim_window_term(void)
{
	REGISTER DISCHANNEL *tr;

	sim_window_killalltraces(FALSE);

	while (sim_window_plotfree != NODISCHANNEL)
	{
		tr = sim_window_plotfree;
		sim_window_plotfree = sim_window_plotfree->nextdischannel;
		if (tr->timelimit > 0) efree((char *)tr->timearray);
		if (tr->statelimit > 0) efree((char *)tr->statearray);
		if (tr->valuelimit > 0) efree((char *)tr->valuearray);
		efree((char *)tr);
	}
	if (sim_window_highlightedtracetotal > 0) efree((char *)sim_window_highlightedtraces);
	if (sim_window_bustracetotal > 0) efree((char *)sim_window_bustraces);
	if (sim_window_sigtotal > 0)
	{
		efree((char *)sim_window_sigvalues);
		sim_window_sigtotal = 0;
	}
}

/*
 * routine to start a simulation window with room for "linecount" traces and associated facet "np".
 * The character handler to use in the waveform window is in "charhandlerwave", the character
 * handler to use in the schematic/layout window is in "charhandlerschem".  Returns true on error.
 */
BOOLEAN sim_window_create(INTBIG linecount, NODEPROTO *np, BOOLEAN (*charhandlerwave)(WINDOWPART*, INTSML, INTBIG),
	BOOLEAN (*charhandlerschem)(WINDOWPART*, INTSML, INTBIG), INTBIG format)
{
	REGISTER WINDOWPART *wavewin, *schemwin;
	REGISTER NODEPROTO *simnp;

	/* sorry, can't have more than 1 simulation window at a time */
	wavewin = sim_window_findwaveform();
	schemwin = sim_window_findschematics();
	simnp = NONODEPROTO;
	if (wavewin != NOWINDOWPART && (wavewin->state&WINDOWSIMULATING) != 0)
		simnp = wavewin->curnodeproto;
	if (schemwin != NOWINDOWPART) simnp = schemwin->curnodeproto;
	if (simnp != NONODEPROTO && simnp != np)
	{
		ttyputerr("Sorry, already simulating facet %s", describenodeproto(simnp));
		return(TRUE);
	}

	/* reinitialize waveform display if number of lines is specified */
	if (linecount > 0)
	{
		sim_window_lines = linecount;
		if (sim_window_lines < 1) sim_window_lines = 1;
		sim_window_killalltraces(TRUE);
		sim_window_topline = 0;
	}

	/* save simulation format */
	sim_window_format = format;

	/* determine the number of visible signals */
	sim_window_vislines = sim_window_lines;

	/* create waveform window if requested */
	if (charhandlerwave != 0)
	{
		if (wavewin == NOWINDOWPART)
		{
			/* get window state information */
			switch (sim_window_state&WAVEPLACE)
			{
				case WAVEPLACECAS:
					wavewin = (WINDOWPART *)asktool(us_tool, "window-new");
					break;
				case WAVEPLACETHOR:
					wavewin = (WINDOWPART *)asktool(us_tool, "window-horiz-new");
					break;
				case WAVEPLACETVER:
					wavewin = (WINDOWPART *)asktool(us_tool, "window-vert-new");
					break;
			}
			if (wavewin == NOWINDOWPART) return(TRUE);
		}
		sim_window_setviewport(wavewin);

		startobjectchange((INTBIG)wavewin, VWINDOWPART);
		(void)setval((INTBIG)wavewin, VWINDOWPART, "state", (wavewin->state & ~WINDOWTYPE) | WAVEFORMWINDOW |
			WINDOWSIMULATING, VINTEGER);
		(void)setval((INTBIG)wavewin, VWINDOWPART, "curnodeproto", (INTBIG)np, VNODEPROTO);
		(void)setval((INTBIG)wavewin, VWINDOWPART, "buttonhandler", (INTBIG)sim_window_buttonhandler, VADDRESS);
		(void)setval((INTBIG)wavewin, VWINDOWPART, "charhandler", (INTBIG)charhandlerwave, VADDRESS);
		(void)setval((INTBIG)wavewin, VWINDOWPART, "redisphandler", (INTBIG)sim_window_redisphandler, VADDRESS);
		endobjectchange((INTBIG)wavewin, VWINDOWPART);
		us_setfacetname(wavewin);
	}

	/* find and setup associated schematics/layout window if requested */
	if (charhandlerschem != 0)
	{
		for(schemwin = el_topwindowpart; schemwin != NOWINDOWPART;
			schemwin = schemwin->nextwindowpart)
		{
			if ((schemwin->state&WINDOWTYPE) != DISPWINDOW &&
				(schemwin->state&WINDOWTYPE) != DISP3DWINDOW) continue;
			if (schemwin->curnodeproto == np) break;
		}
		if (schemwin != NOWINDOWPART)
		{
			startobjectchange((INTBIG)schemwin, VWINDOWPART);
			(void)setval((INTBIG)schemwin, VWINDOWPART, "state", schemwin->state |
				WINDOWSIMULATING, VINTEGER);
			(void)setval((INTBIG)schemwin, VWINDOWPART, "charhandler",
				(INTBIG)charhandlerschem, VADDRESS);
			endobjectchange((INTBIG)schemwin, VWINDOWPART);
		}
	}
	return(FALSE);
}

/*
 * Routine to stop simulation.
 */
void sim_window_stopsimulation(void)
{
	REGISTER WINDOWPART *schemwin, *wavewin;

	/* disable simulation control in schematics/layout window */
	schemwin = sim_window_findschematics();
	if (schemwin != NOWINDOWPART)
	{
		startobjectchange((INTBIG)schemwin, VWINDOWPART);
		(void)setval((INTBIG)schemwin, VWINDOWPART, "state", schemwin->state &
			~WINDOWSIMULATING, VINTEGER);
		(void)setval((INTBIG)schemwin, VWINDOWPART, "charhandler",
			(INTBIG)DEFAULTCHARHANDLER, VADDRESS);
		endobjectchange((INTBIG)schemwin, VWINDOWPART);
	}

	/* disable waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin != NOWINDOWPART)
	{
		startobjectchange((INTBIG)wavewin, VWINDOWPART);
		(void)setval((INTBIG)wavewin, VWINDOWPART, "state", wavewin->state &
			~WINDOWSIMULATING, VINTEGER);
		endobjectchange((INTBIG)wavewin, VWINDOWPART);
	}
}

/*
 * routine that returns:
 *  0                                    if there is no active simulation
 *  SIMWINDOWWAVEFORM                    if simulating and have a waveform window
 *  SIMWINDOWSCHEMATIC                   if simulating and have a schematics window
 *  SIMWINDOWWAVEFORM|SIMWINDOWSCHEMATIC if simulating and have both windows
 * If there is active simulation, the facet being simulated is returned in "np"
 */
INTBIG sim_window_isactive(NODEPROTO **np)
{
	REGISTER INTBIG activity;
	REGISTER WINDOWPART *wavewin, *schemwin;

	*np = NONODEPROTO;
	activity = 0;
	schemwin = sim_window_findschematics();
	if (schemwin != NOWINDOWPART)
	{
		activity |= SIMWINDOWSCHEMATIC;
		*np = schemwin->curnodeproto;
	}
	wavewin = sim_window_findwaveform();
	if (wavewin != NOWINDOWPART && (wavewin->state&WINDOWSIMULATING) != 0)
	{
		activity |= SIMWINDOWWAVEFORM;
		*np = wavewin->curnodeproto;
	}
	return(activity);
}

/*
 * this procedure redraws everything, including the waveforms.
 */
void sim_window_redraw(void)
{
	REGISTER WINDOWPART *wavewin;

	/* get the waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return;
	sim_window_redisphandler(wavewin);
}

/*
 * routine that changes the number of trace lines in the simulation window to "count"
 */
void sim_window_setlines(INTBIG count)
{
	if (count <= 0) count = 1;
	sim_window_lines = count;
}

/*
 * routine that returns the number of signals in the simulation window
 */
INTBIG sim_window_getlines(void)
{
	return(sim_window_lines);
}

/*
 * routine that returns the number of visible signals in the simulation window
 */
INTBIG sim_window_getvislines(void)
{
	return(sim_window_vislines);
}

/*
 * routine that changes the top visible trace line in the simulation window to "count"
 */
void sim_window_settopline(INTBIG top)
{
	if (top < 0) top = 0;
	sim_window_topline = top;
}

/*
 * routine that returns the top visible signal in the simulation window.
 */
INTBIG sim_window_gettopline(void)
{
	return(sim_window_topline);
}

/********************************* CONTROL OF TRACES *********************************/

/*
 * routine to create a new trace to be displayed in window location
 * "position".  Its name is set to "name", and "userdata" is
 * associated with it.  A pointer to the trace is returned (zero on error).
 */
INTBIG sim_window_newtrace(INTBIG position, char *name, INTBIG userdata)
{
	REGISTER DISCHANNEL *tr, *otr, *lasttr;

	/* create a new trace */
	tr = sim_window_allocdischannel();
	if (tr == NODISCHANNEL) return(0);
	tr->nodeptr = userdata;

	/* set name and convert if it has array index form */
	(void)allocstring(&tr->name, name, sim_tool->cluster);
	(void)allocstring(&tr->origname, name, sim_tool->cluster);

	/* put in active list */
	lasttr = NODISCHANNEL;
	for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
		lasttr = otr;
	sim_window_addtrace(tr, lasttr);

	if (position >= 0) tr->position = position; else
	{
		tr->position = -1;
		sim_window_renumberlines();
	}

	/* save the new configuration */
	sim_window_savesignalorder();

	return((INTBIG)tr);
}

/*
 * Routine to open/close a bus.  Returns true on error.
 */
BOOLEAN sim_window_buscommand(void)
{
	REGISTER DISCHANNEL *tr;
	REGISTER INTBIG i, count, newtr;
	INTBIG *tracelist;

	if (sim_window_highlightedtracecount == 0) return(TRUE);
	if (sim_window_highlightedtracecount == 1)
	{
		tr = sim_window_highlightedtraces[0];
		switch (tr->flags&TRACETYPE)
		{
			case TRACEISDIGITAL:
			case TRACEISANALOG:
				ttyputerr(_("Must select multiple signals to pack into a bus"));
				break;
			case TRACEISBUS:
				ttyputmsg(_("Double-click the bus name to open it"));
				break;
			case TRACEISOPENBUS:
				ttyputmsg(_("Double-click the bus name to close it"));
				break;
		}
		return(TRUE);
	}

	/* copy highlighted traces to an array and remove highlighting */
	count = sim_window_highlightedtracecount;
	tracelist = (INTBIG *)emalloc(count * SIZEOFINTBIG, sim_tool->cluster);
	if (tracelist == 0) return(TRUE);
	for(i=0; i<count; i++)
		tracelist[i] = (INTBIG)sim_window_highlightedtraces[i];
	sim_window_cleartracehighlight();

	/* make the bus */
	newtr = sim_window_makebus(count, tracelist, 0);
	efree((char *)tracelist);
	if (newtr == 0) return(TRUE);
	sim_window_addhighlighttrace(newtr);

	/* redisplay */
	sim_window_redraw();

	/* save the new configuration */
	sim_window_savesignalorder();
	return(FALSE);
}

INTBIG sim_window_makebus(INTBIG count, INTBIG *traces, char *busname)
{
	REGISTER DISCHANNEL *tr, *lasttr, *newtr, **tracelist;
	REGISTER INTBIG i, flags;

	/* check for errors */
	tracelist = (DISCHANNEL **)traces;
	for(i=0; i<count; i++)
	{
		flags = tracelist[i]->flags & TRACETYPE;
		if (flags == TRACEISBUS || flags == TRACEISOPENBUS)
		{
			ttyputerr(_("Cannot place a bus into another bus"));
			return(0);
		}
		if (tracelist[i]->buschannel != NODISCHANNEL)
		{
			ttyputerr(_("Some of these signals are already in a bus"));
			return(0);
		}
	}
	if (count > MAXSIMWINDOWBUSWIDTH)
	{
		ttyputerr(_("Can only handle busses that are %d bits wide"), MAXSIMWINDOWBUSWIDTH);
		return(0);
	}

	/* find place to insert bus */
	lasttr = NODISCHANNEL;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		for(i=0; i<count; i++)
			if (tracelist[i] == tr) break;
		if (i < count) break;
		lasttr = tr;
	}

	/* create a new trace that combines all of these */
	newtr = sim_window_allocdischannel();
	if (newtr == NODISCHANNEL) return(0);
	if (busname != 0) (void)allocstring(&newtr->name, busname, sim_tool->cluster); else
		(void)allocstring(&newtr->name, sim_window_namesignals(count, tracelist),
			sim_tool->cluster);
	(void)allocstring(&newtr->origname, newtr->name, sim_tool->cluster);
	newtr->nodeptr = 0;
	newtr->flags = TRACEISBUS;

	/* insert it after "lasttr" */
	sim_window_addtrace(newtr, lasttr);

	/* flag the signals as part of this bus */
	for(i=0; i<count; i++)
	{
		tr = tracelist[i];
		tr->buschannel = newtr;
		tr->flags |= TRACENOTDRAWN;
	}

	/* renumber the lines in the display */
	sim_window_renumberlines();
	return((INTBIG)newtr);
}

/*
 * Routine to generate a bus name for the "count" signals in "list".
 */
char *sim_window_namesignals(INTBIG count, DISCHANNEL **list)
{
	REGISTER INTBIG i, j, rootlen, lastvalue, thisvalue, direction;
	REGISTER DISCHANNEL *tr, *ptr;

	/* look for common root */
	rootlen = 0;
	for(i=1; i<count; i++)
	{
		ptr = list[i-1];
		tr = list[i];
		for(j=0; ; j++)
		{
			if (ptr->name[j] == 0 || tr->name[0] == 0) break;
			if (ptr->name[j] != tr->name[j]) break;
		}
		if (i == 1 || j < rootlen) rootlen = j;
	}

	/* if there is a common root, make sure it is proper */
	if (rootlen > 0)
	{
		tr = list[0];
		if (tr->name[rootlen-1] != '[')
		{
			for(i=0; i<count; i++)
			{
				tr = list[i];
				if (isdigit(tr->name[rootlen]) == 0) break;
			}
			if (i < count) rootlen = 0;
		}
	}

	/* if there is a common root, use it */
	if (rootlen > 0)
	{
		(void)initinfstr();
		tr = list[0];
		for(j=0; j<rootlen; j++) (void)addtoinfstr(tr->name[j]);

		for(i=0; i<count; i++)
		{
			tr = list[i];
			if (i != 0) (void)addtoinfstr(',');
			for(j=rootlen; tr->name[j] != ']' && tr->name[j] != 0; j++)
				(void)addtoinfstr(tr->name[j]);

			/* see if there is a sequence */
			lastvalue = atoi(&tr->name[rootlen]);
			direction = 0;
			for(j=i+1; j<count; j++)
			{
				thisvalue = atoi(&list[j]->name[rootlen]);
				if (direction == 0)
				{
					if (lastvalue+1 == thisvalue) direction = 1; else
						if (lastvalue-1 == thisvalue) direction = -1; else
							break;
				}
				if (lastvalue + direction != thisvalue) break;
				lastvalue = thisvalue;
			}
			if (j > i+1)
			{
				(void)formatinfstr(":%ld", thisvalue);
				i = j;
			}
		}
		if (tr->name[rootlen-1] == '[') (void)addtoinfstr(']');
		return(returninfstr());
	}

	/* no common root: concatenate all signal names */
	(void)initinfstr();
	for(i=0; i<count; i++)
	{
		if (i != 0) (void)addtoinfstr(',');
		tr = list[i];
		(void)addstringtoinfstr(tr->name);
	}
	return(returninfstr());
}

void sim_window_savesignalorder(void)
{
	REGISTER DISCHANNEL *tr, *otr;
	NODEPROTO *np;
	char **strings;
	INTBIG count, i, j;

	if (sim_window_isactive(&np) == 0) return;
	for(count = 0, tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
		if ((tr->flags&TRACENOTDRAWN) == 0 && tr->buschannel == NODISCHANNEL) count++;
	if (count == 0)
	{
		if (getvalkey((INTBIG)np, VNODEPROTO, VSTRING|VISARRAY, sim_window_signalorder_key) != NOVARIABLE)
			(void)delvalkey((INTBIG)np, VNODEPROTO, sim_window_signalorder_key);
	} else
	{
		strings = (char **)emalloc(count * (sizeof (char *)), sim_tool->cluster);
		for(i=0, j=0, tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel, i++)
		{
			if ((tr->flags&TRACENOTDRAWN) != 0 || tr->buschannel != NODISCHANNEL) continue;
			if ((tr->flags&TRACETYPE) == TRACEISBUS || (tr->flags&TRACETYPE) == TRACEISOPENBUS)
			{
				/* construct name for this bus */
				(void)initinfstr();
				(void)addstringtoinfstr(tr->origname);
				for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
				{
					if (otr->buschannel != tr) continue;
					(void)addtoinfstr('\t');
					(void)addstringtoinfstr(otr->origname);
				}
				(void)allocstring(&strings[j++], returninfstr(), sim_tool->cluster);
			} else
			{
				(void)allocstring(&strings[j++], tr->origname, sim_tool->cluster);
			}
		}
		(void)setvalkey((INTBIG)np, VNODEPROTO, sim_window_signalorder_key, (INTBIG)strings,
			VSTRING|VISARRAY|(count<<VLENGTHSH));
		for(i=0; i<count; i++) efree((char *)strings[i]);
		efree((char *)strings);
	}
}

/*
 * routine to load trace "tri" with "count" digital events.  The events occur
 * at time "time" and have state "state".  "State" is encoded with a
 * level in the upper 8 bits (LOGIC_LOW, LOGIC_HIGH, or LOGIC_X) and a
 * strength in the lower 8 bits (OFF_STRENGTH, NODE_STRENGTH, GATE_STRENGTH,
 * or VDD_STRENGTH).
 */
void sim_window_loaddigtrace(INTBIG tri, INTBIG count, double *time, INTSML *state)
{
	REGISTER INTBIG wanted, i;
	REGISTER DISCHANNEL *tr;

	/* ensure space in the arrays */
	tr = (DISCHANNEL *)tri;
	if (tr == NODISCHANNEL || tr == 0) return;
	if (count > tr->timelimit)
	{
		if (tr->timelimit > 0)
		{
			efree((char *)tr->timearray);
			tr->timelimit = 0;
		}
		wanted = count + 10;
		tr->timearray = (double *)emalloc(wanted * (sizeof (double)), sim_tool->cluster);
		if (tr->timearray == 0) return;
		tr->timelimit = wanted;
	}
	if (count > tr->statelimit)
	{
		if (tr->statelimit > 0)
		{
			efree((char *)tr->statearray);
			tr->statelimit = 0;
		}
		wanted = count + 10;
		tr->statearray = (INTBIG *)emalloc(wanted * SIZEOFINTBIG, sim_tool->cluster);
		if (tr->statearray == 0) return;
		tr->statelimit = wanted;
	}

	/* load the data */
	for(i=0; i<count; i++)
	{
		tr->timearray[i] = time[i];
		tr->statearray[i] = state[i];
	}
	tr->flags = (tr->flags & ~TRACETYPE) | TRACEISDIGITAL;
	tr->numsteps = count;
}

/*
 * routine to load trace "tri" with "count" analog events.  The events occur
 * at time "time" and have value "value".
 */
void sim_window_loadanatrace(INTBIG tri, INTBIG count, double *time, float *value)
{
	REGISTER INTBIG wanted, i;
	REGISTER DISCHANNEL *tr;

	/* ensure space in the arrays */
	tr = (DISCHANNEL *)tri;
	if (tr == NODISCHANNEL || tr == 0) return;
	if (count > tr->timelimit)
	{
		if (tr->timelimit > 0)
		{
			efree((char *)tr->timearray);
			tr->timelimit = 0;
		}
		wanted = count + 10;
		tr->timearray = (double *)emalloc(wanted * (sizeof (double)), sim_tool->cluster);
		if (tr->timearray == 0) return;
		tr->timelimit = wanted;
	}
	if (count > tr->valuelimit)
	{
		if (tr->valuelimit > 0)
		{
			efree((char *)tr->valuearray);
			tr->valuelimit = 0;
		}
		wanted = count + 10;
		tr->valuearray = (float *)emalloc(wanted * (sizeof (float)), sim_tool->cluster);
		if (tr->valuearray == 0) return;
		tr->valuelimit = wanted;
	}

	/* load the data */
	for(i=0; i<count; i++)
	{
		tr->timearray[i] = time[i];
		tr->valuearray[i] = value[i];
	}
	tr->flags = (tr->flags & ~TRACETYPE) | TRACEISANALOG;
	tr->color = RED;
	tr->numsteps = count;
}

/*
 * Sets the analog range to "low"-to-"high".  This is the displayed vertical range.
 */
void sim_window_setanarange(float low, float high)
{
	sim_window_anadislow = low;
	sim_window_anadishigh = high;
	sim_window_anadisrange = high - low;
}

/*
 * Gets the analog range into "low"-to-"high".  This is the displayed vertical range.
 */
void sim_window_getanarange(float *low, float *high)
{
	*low = sim_window_anadislow;
	*high = sim_window_anadishigh;
}

/*
 * Sets the analog extent to "low"-to-"high".  This is the absolute extent of the data.
 */
void sim_window_setanaextents(float low, float high)
{
	sim_window_analow = low;
	sim_window_anahigh = high;
	sim_window_anarange = high - low;
}

/*
 * Gets the analog extent in "low"-to-"high".  This is the absolute extent of the data.
 */
void sim_window_getanaextents(float *low, float *high)
{
	*low = sim_window_analow;
	*high = sim_window_anahigh;
}

void sim_window_settraceline(INTBIG tri, INTBIG line)
{
	REGISTER DISCHANNEL *tr;

	tr = (DISCHANNEL *)tri;
	if (tr == NODISCHANNEL || tr == 0) return;
	tr->position = line;
}

/*
 * routine to delete the trace "tri"
 */
void sim_window_killtrace(INTBIG tri)
{
	REGISTER DISCHANNEL *tr, *str, *ltr, *otr;
	REGISTER INTBIG position;

	tr = (DISCHANNEL *)tri;
	if (tr == NODISCHANNEL || tr == 0) return;
	position = tr->position;
	ltr = NODISCHANNEL;
	for(str = sim_window_plot; str != NODISCHANNEL; str = str->nextdischannel)
	{
		if (str == tr) break;
		ltr = str;
	}
	if (ltr == NODISCHANNEL) sim_window_plot = tr->nextdischannel; else
		ltr->nextdischannel = tr->nextdischannel;
	tr->nextdischannel = sim_window_plotfree;
	sim_window_plotfree = tr;
	efree(tr->name);
	efree(tr->origname);

	/* if a bus was deleted, make it's individual signals be visible and independent */
	if ((tr->flags&TRACETYPE) == TRACEISBUS || (tr->flags&TRACETYPE) == TRACEISOPENBUS)
	{
		for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
		{
			if (otr->buschannel != tr) continue;
			otr->buschannel = NODISCHANNEL;
			otr->flags &= ~TRACENOTDRAWN;
		}
	}

	/* renumber the lines in the display */
	sim_window_renumberlines();

	/* save the new configuration */
	sim_window_savesignalorder();
}

/*
 * routine to delete all traces
 */
void sim_window_killalltraces(BOOLEAN save)
{
	REGISTER DISCHANNEL *tr, *nexttr;

	/* return all plot channels to the free list */
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = nexttr)
	{
		nexttr = tr->nextdischannel;
		tr->nextdischannel = sim_window_plotfree;
		sim_window_plotfree = tr;
		efree(tr->name);
		efree(tr->origname);
	}
	sim_window_plot = NODISCHANNEL;
	sim_window_highlightedtracecount = 0;

	/* save the new configuration */
	if (save) sim_window_savesignalorder();
}

/********************************* TRACE INFORMATION *********************************/

INTBIG *sim_window_getbustraces(INTBIG tri)
{
	REGISTER DISCHANNEL *tr, *otr;
	REGISTER INTBIG count, newtotal, i;
	REGISTER DISCHANNEL **newbustraces;
	static INTBIG nullbustraces[1];

	tr = (DISCHANNEL *)tri;
	count = 0;
	for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
	{
		if (otr->buschannel != tr) continue;
		if (count+1 >= sim_window_bustracetotal)
		{
			newtotal = sim_window_bustracetotal*2;
			if (newtotal == 0) newtotal = 5;
			if (count+1 > newtotal) newtotal = count+1;
			newbustraces = (DISCHANNEL **)emalloc(newtotal * (sizeof (DISCHANNEL *)),
				sim_tool->cluster);
			if (newbustraces == 0) return(0);
			for(i=0; i<count; i++)
				newbustraces[i] = sim_window_bustraces[i];
			if (sim_window_bustracetotal > 0) efree((char *)sim_window_bustraces);
			sim_window_bustraces = newbustraces;
			sim_window_bustracetotal = newtotal;
		}
		sim_window_bustraces[count++] = otr;
		sim_window_bustraces[count] = 0;
	}
	if (count == 0)
	{
		nullbustraces[0] = 0;
		return(nullbustraces);
	}
	return((INTBIG *)sim_window_bustraces);
}

/*
 * routine to return the line number associated with trace "tri"
 */
INTBIG sim_window_gettraceline(INTBIG tri)
{
	REGISTER DISCHANNEL *tr;

	tr = (DISCHANNEL *)tri;
	if (tr == NODISCHANNEL || tr == 0) return(0);
	return(tr->position);
}

/*
 * routine to return the name of trace "tri".
 */
char *sim_window_gettracename(INTBIG tri)
{
	REGISTER DISCHANNEL *tr;

	tr = (DISCHANNEL *)tri;
	if (tr == NODISCHANNEL || tr == 0) return("");
	return(tr->origname);
}

/*
 * routine to return the "user" data associated with trace "tri"
 */
INTBIG sim_window_gettracedata(INTBIG tri)
{
	REGISTER DISCHANNEL *tr;

	tr = (DISCHANNEL *)tri;
	if (tr == NODISCHANNEL || tr == 0) return(0);
	return(tr->nodeptr);
}

float sim_window_getanatracevalue(INTBIG tri, double time)
{
	REGISTER DISCHANNEL *tr;
	REGISTER INTBIG j;
	REGISTER float lastvalue, thisvalue, v;
	REGISTER double lasttime, thistime;

	tr = (DISCHANNEL *)tri;
	if (tr == NODISCHANNEL || tr == 0) return(0.0);

	/* must be an analog trace */
	if ((tr->flags&TRACETYPE) != TRACEISANALOG) return(0.0);

	for(j=0; j<tr->numsteps; j++)
	{
		thistime = tr->timearray[j];
		thisvalue = tr->valuearray[j] * sim_window_anarange + sim_window_analow;
		if (thistime == time) return(thisvalue);
		if (time > thistime)
		{
			lasttime = thistime;
			lastvalue = thisvalue;
			continue;
		}
		if (j == 0) return(thisvalue);
		v = (float)(lastvalue + (time-lasttime) * (thisvalue-lastvalue) / (thistime-lasttime));
		return(v);
	}

	return(lastvalue);
}

INTBIG sim_window_findtrace(char *name)
{
	REGISTER DISCHANNEL *tr;
	REGISTER char *tempname;

	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		if (namesame(tr->name, name) == 0) return((INTBIG)tr);
	}

	/* keywords get the letters "NV" added to the end */
	(void)initinfstr();
	(void)addstringtoinfstr(name);
	(void)addstringtoinfstr("NV");
	tempname = returninfstr();
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		if (namesame(tr->name, tempname) == 0) return((INTBIG)tr);
	}

	/* handle "v(" and "l(" prefixes of HSPICE (see also "dbtext.c:getcomplexnetwork()") */
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		if ((tr->name[0] == 'l' || tr->name[0] == 'v') && tr->name[1] == '(')
		{
			if (namesame(&tr->name[2], name) == 0) return((INTBIG)tr);
		}
	}

	/* name not found in simulator */
	return(0);
}

void sim_window_inittraceloop(void)
{
	sim_window_loop = sim_window_plot;
}

void sim_window_inittraceloop2(void)
{
	sim_window_loop2 = sim_window_plot;
}

INTBIG sim_window_nexttraceloop(void)
{
	REGISTER DISCHANNEL *tr;

	tr = sim_window_loop;
	if (tr == NODISCHANNEL) return(0);
	sim_window_loop = tr->nextdischannel;
	return((INTBIG)tr);
}

INTBIG sim_window_nexttraceloop2(void)
{
	REGISTER DISCHANNEL *tr;

	tr = sim_window_loop2;
	if (tr == NODISCHANNEL) return(0);
	sim_window_loop2 = tr->nextdischannel;
	return((INTBIG)tr);
}

/********************************* TRACE HIGHLIGHTING *********************************/

/*
 * Routine to clear all highlighting in the waveform window.
 */
void sim_window_cleartracehighlight(void)
{
	REGISTER WINDOWPART *wavewin;
	REGISTER INTBIG i;

	/* get the waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return;
	sim_window_setviewport(wavewin);

	/* erase highlighting */
	for(i=0; i<sim_window_highlightedtracecount; i++)
		sim_window_plottrace(sim_window_highlightedtraces[i], wavewin, 2);
	sim_window_highlightedtracecount = 0;
}

/*
 * routine that adds trace "tri" to the highlighted traces in the waveform window
 */
void sim_window_addhighlighttrace(INTBIG tri)
{
	REGISTER DISCHANNEL *tr, **newtraces;
	REGISTER WINDOWPART *wavewin;
	REGISTER INTBIG newtotal, i;

	tr = (DISCHANNEL *)tri;

	/* get the waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return;
	sim_window_setviewport(wavewin);

	/* erase highlighting */
	for(i=0; i<sim_window_highlightedtracecount; i++)
		sim_window_plottrace(sim_window_highlightedtraces[i], wavewin, 2);

	/* add this signal to highlighting */
	if (sim_window_highlightedtracecount+1 >= sim_window_highlightedtracetotal)
	{
		newtotal = sim_window_highlightedtracetotal * 2;
		if (newtotal == 0) newtotal = 2;
		if (sim_window_highlightedtracecount+1 > newtotal)
			newtotal = sim_window_highlightedtracecount+1;
		newtraces = (DISCHANNEL **)emalloc(newtotal * (sizeof (DISCHANNEL *)), sim_tool->cluster);
		if (newtraces == 0) return;
		for(i=0; i<sim_window_highlightedtracecount; i++)
			newtraces[i] = sim_window_highlightedtraces[i];
		if (sim_window_highlightedtracetotal > 0) efree((char *)sim_window_highlightedtraces);
		sim_window_highlightedtraces = newtraces;
		sim_window_highlightedtracetotal = newtotal;
	}
	sim_window_highlightedtraces[sim_window_highlightedtracecount++] = tr;
	sim_window_highlightedtraces[sim_window_highlightedtracecount] = 0;

	/* show highlighting */
	for(i=0; i<sim_window_highlightedtracecount; i++)
		sim_window_plottrace(sim_window_highlightedtraces[i], wavewin, 1);
}

/*
 * routine that removes trace "tri" from the highlighted traces in the waveform window
 */
void sim_window_deletehighlighttrace(INTBIG tri)
{
	REGISTER DISCHANNEL *tr;
	REGISTER WINDOWPART *wavewin;
	REGISTER INTBIG k, j, i;

	tr = (DISCHANNEL *)tri;

	/* get the waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return;
	sim_window_setviewport(wavewin);

	/* find this signal in the highlighting */
	for(j=0; j<sim_window_highlightedtracecount; j++)
		if (tr == sim_window_highlightedtraces[j]) break;
	if (j < sim_window_highlightedtracecount)
	{
		/* erase highlighting */
		for(i=0; i<sim_window_highlightedtracecount; i++)
			sim_window_plottrace(sim_window_highlightedtraces[i], wavewin, 2);

		/* remove the signal from the list */
		k = 0;
		for(i=0; i<sim_window_highlightedtracecount; i++)
		{
			if (i == j) continue;
			sim_window_highlightedtraces[k++] = sim_window_highlightedtraces[i];
		}
		sim_window_highlightedtracecount = k;

		/* show highlighting */
		for(i=0; i<sim_window_highlightedtracecount; i++)
			sim_window_plottrace(sim_window_highlightedtraces[i], wavewin, 1);
	}
}

/*
 * routine that returns the highlighted trace in the waveform window
 */
INTBIG sim_window_gethighlighttrace(void)
{
	if (sim_window_highlightedtracecount == 0) return(0);
	return((INTBIG)sim_window_highlightedtraces[0]);
}

/*
 * routine that returns a 0-terminated list of highlighted traces in the waveform window
 */
INTBIG *sim_window_gethighlighttraces(void)
{
	static INTBIG nothing[1];

	nothing[0] = 0;
	if (sim_window_highlightedtracecount == 0) return(nothing);
	return((INTBIG *)sim_window_highlightedtraces);
}

/*
 * Routine to update associated layout windows when the main cursor changes
 */
void sim_window_updatelayoutwindow(void)
{
	REGISTER DISCHANNEL *tr;
	REGISTER NETWORK *net;
	REGISTER ARCINST *ai;
	REGISTER INTBIG j, fx, fy, tx, ty, texture, strength;
	REGISTER NODEPROTO *simnt;
	REGISTER WINDOWPART *schemwin;
	static POLYGON *poly = NOPOLYGON;

	/* make sure there is a layout/schematic window being simulated */
	schemwin = sim_window_findschematics();
	if (schemwin == NOWINDOWPART) return;
	simnt = schemwin->curnodeproto;

	/* reset all values on networks */
	for(net = simnt->firstnetwork; net != NONETWORK; net = net->nextnetwork)
		net->temp1 = -1;

	/* assign values from simulation window traces to networks */
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;

		/* can only handle digital waveforms right now */
		if ((tr->flags&TRACETYPE) != TRACEISDIGITAL) continue;

		/* find the proper data for the time of the main cursor */
		for(j=1; j<tr->numsteps; j++)
			if (sim_window_maincursor < tr->timearray[j]) break;
		j--;

		/* set simulation value on the network in the associated layout/schematic window */
		net = getcomplexnetwork(tr->name, simnt);
		if (net != NONETWORK && j < tr->numsteps) net->temp1 = tr->statearray[j];
	}

	/* redraw all arcs in the layout/schematic window */
	sim_window_desc.bits = LAYERA;
	for(ai = simnt->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		net = ai->network;
		if (net == NONETWORK) continue;
		if (net->temp1 == -1) continue;

		/* get extent of arc */
		fx = ai->end[0].xpos;   fy = ai->end[0].ypos;
		tx = ai->end[1].xpos;   ty = ai->end[1].ypos;
		if (ai->proto == sch_wirearc)
		{
			/* for schematic wires, ask technology about drawing so negating bubbles work */
			if (poly == NOPOLYGON) poly = allocstaticpolygon(4, sim_tool->cluster);
			(void)arcpolys(ai, NOWINDOWPART);
			shapearcpoly(ai, 0, poly);
			fx = poly->xv[0];   fy = poly->yv[0];
			tx = poly->xv[1];   ty = poly->yv[1];
		}

		if ((sim_window_state&FULLSTATE) != 0)
		{
			/* 12-state display: determine trace texture */
			/* determine trace texture */
			strength = net->temp1 & 0377;
			if (strength == 0) texture = -1; else
				if (strength <= NODE_STRENGTH) texture = 1; else
					if (strength <= GATE_STRENGTH) texture = 0; else
						texture = 2;

			/* reset background arc if trace is textured */
			if (texture != 0)
			{
				sim_window_desc.col = ALLOFF;
				us_wanttodraw(fx, fy, tx, ty, schemwin, &sim_window_desc, 0);
				if (texture < 0) texture = 0;
			}

			/* determine trace color */
			switch (net->temp1 >> 8)
			{
				case LOGIC_LOW:  sim_window_desc.col = sim_colorlevellow;     break;
				case LOGIC_X:    sim_window_desc.col = sim_colorlevelundef;   break;
				case LOGIC_HIGH: sim_window_desc.col = sim_colorlevelhigh;    break;
			}
		} else
		{
			/* 2-state display */
			texture = 0;
			if ((net->temp1 >> 8) == LOGIC_HIGH) sim_window_desc.col = sim_colorstrengthgate; else
				sim_window_desc.col = sim_colorstrengthoff;
		}

		/* draw the trace on the arc */
		us_wanttodraw(fx, fy, tx, ty, schemwin, &sim_window_desc, texture);
	}
}

/********************************* TIME AND CURSOR CONTROL *********************************/

/*
 * routine to set the time for the "main" cursor in the simulation window
 */
void sim_window_setmaincursor(double time)
{
	REGISTER WINDOWPART *wavewin;

	/* get the waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return;

	sim_window_maincursor = time;
	sim_window_updatelayoutwindow();
	sim_window_drawcursors(wavewin);
}

/*
 * routine to return the time of the "main" cursor in the simulation window
 */
double sim_window_getmaincursor(void)
{
	return(sim_window_maincursor);
}

/*
 * routine to set the time for the "extension" cursor in the simulation window
 */
void sim_window_setextensioncursor(double time)
{
	REGISTER WINDOWPART *wavewin;

	/* get the waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return;

	sim_window_extensioncursor = time;
	sim_window_drawcursors(wavewin);
}

/*
 * routine to return the time of the "extension" cursor in the simulation window
 */
double sim_window_getextensioncursor(void)
{
	return(sim_window_extensioncursor);
}

/*
 * routine to set the simulation window to run from time "min" to time "max"
 */
void sim_window_settimerange(double min, double max)
{
	sim_window_mintime = min;
	sim_window_maxtime = max;
}

/*
 * routine to return the range of time displayed in the simulation window
 */
void sim_window_gettimerange(double *min, double *max)
{
	*min = sim_window_mintime;
	*max = sim_window_maxtime;
}

/*
 * routine to return the range of time that is in the data
 */
void sim_window_gettimeextents(double *min, double *max)
{
	REGISTER DISCHANNEL *tr;
	double time;
	REGISTER INTBIG j;

	/* initially enclose the cursors */
	*min = sim_window_maincursor;
	*max = sim_window_extensioncursor;
	if (*max < *min)
	{
		*min = sim_window_extensioncursor;
		*max = sim_window_maincursor;
	}

	/* also enclose all of the data */
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		for(j=0; j<tr->numsteps; j++)
		{
			time = tr->timearray[j];
			if (time < *min) *min = time;
			if (time > *max) *max = time;
		}
	}
}

/********************************* WINDOW METHODS *********************************/

BOOLEAN sim_window_eachwdown(INTBIG x, INTBIG y)
{
	REGISTER WINDOWPART *wavewin;

	/* get the waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return(FALSE);

	sim_window_scaletowindow(wavewin, &x, &y);
	sim_window_maincursor = sim_window_xpostotime(x);
	if (sim_window_maincursor < sim_window_mintime)
		sim_window_maincursor = sim_window_mintime;
	if (sim_window_maincursor > sim_window_maxtime)
		sim_window_maincursor = sim_window_maxtime;
	sim_window_drawcursors(wavewin);
	sim_window_updatelayoutwindow();
	return(FALSE);
}

BOOLEAN sim_window_eachbdown(INTBIG x, INTBIG y)
{
	REGISTER WINDOWPART *wavewin;

	/* get the waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return(FALSE);

	sim_window_scaletowindow(wavewin, &x, &y);
	sim_window_extensioncursor = sim_window_xpostotime(x);
	if (sim_window_extensioncursor < sim_window_mintime)
		sim_window_extensioncursor = sim_window_mintime;
	if (sim_window_extensioncursor > sim_window_maxtime)
		sim_window_extensioncursor = sim_window_maxtime;
	sim_window_drawcursors(wavewin);
	return(FALSE);
}

void sim_window_hthumbtrackingcallback(INTBIG delta)
{
	REGISTER INTBIG thumbarea;
	double mintime, maxtime, range;

	thumbarea = sim_waveformwindow->usehx - sim_waveformwindow->uselx - DISPLAYSLIDERSIZE*2;
	sim_window_gettimerange(&mintime, &maxtime);
	range = maxtime * 2.0;
	mintime = (sim_waveformwindow->thumblx + delta -
		(sim_waveformwindow->uselx+DISPLAYSLIDERSIZE+2)) * range / thumbarea;
	if (mintime < 0.0) mintime = 0.0;

	if (mintime == sim_window_mintime) return;
	sim_window_maxtime += mintime - sim_window_mintime;
	sim_window_mintime = mintime;
	if (sim_window_format == ALS) (void)simals_initialize_simulator(TRUE); else
		sim_window_redraw();
}

void sim_window_vthumbtrackingcallback(INTBIG delta)
{
	REGISTER INTBIG thumbarea, point;

	sim_window_deltasofar += delta;
	thumbarea = sim_waveformwindow->usehy - sim_waveformwindow->usely - DISPLAYSLIDERSIZE*2;
	point = (sim_waveformwindow->usehy - DISPLAYSLIDERSIZE -
		(sim_window_initialthumb + sim_window_deltasofar)) *
			sim_window_lines / thumbarea;
	if (point < 0) point = 0;
	if (point > sim_window_lines-sim_window_vislines)
		point = sim_window_lines-sim_window_vislines;
	if (point == sim_window_topline) return;
	sim_window_topline = point;
	sim_window_redisphandler(sim_waveformwindow);
}

/*
 * Routine for drawing highlight around dragged trace name.
 */
void sim_window_drawtracedrag(WINDOWPART *wavewin, BOOLEAN on)
{
	INTBIG y_pos, temp_trace_spacing, line, lx, hx, ly, hy;

	/* compute positions */
	temp_trace_spacing = 540 / sim_window_vislines;
	line = sim_window_tracedragtr->position - sim_window_topline;
	if (line < 0 || line >= sim_window_vislines) return;
	y_pos = (sim_window_vislines - 1 - line) * temp_trace_spacing +
		31 + sim_window_tracedragyoff;
	ly = y_pos + sim_window_tracedragtr->nameoff + 1;
	hy = ly + sim_window_txthei;
	lx = 0;
	hx = 112;

	sim_window_setmask(LAYERH);
	if (!on) sim_window_setcolor(ALLOFF); else
		sim_window_setcolor(HIGHLIT);
	sim_window_moveto(lx, ly);
	sim_window_drawto(wavewin, hx, ly);
	sim_window_drawto(wavewin, hx, hy);
	sim_window_drawto(wavewin, lx, hy);
	sim_window_drawto(wavewin, lx, ly);
}

/*
 * Routine for tracking the drag of signal names in the waveform window.
 */
BOOLEAN sim_window_eachdown(INTBIG x, INTBIG y)
{
	REGISTER WINDOWPART *wavewin;

	/* get the waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return(FALSE);

	sim_window_scaletowindow(wavewin, &x, &y);
	sim_window_drawtracedrag(wavewin, FALSE);
	sim_window_tracedragyoff = y - sim_window_initialy;
	sim_window_drawtracedrag(wavewin, TRUE);
	return(FALSE);
}

/*
 * Routine for drawing highlight around selected trace names.
 */
void sim_window_drawtraceselect(WINDOWPART *wavewin, INTBIG y1, INTBIG y2, BOOLEAN on)
{
	INTBIG x, ly, hy;

	/* compute positions */
	if (y1 == y2) return;
	ly = mini(y1, y2);
	hy = maxi(y1, y2);
	x = sim_window_initialx;

	sim_window_setmask(LAYERH);
	if (!on) sim_window_setcolor(ALLOFF); else
		sim_window_setcolor(HIGHLIT);
	sim_window_moveto(x, ly);
	sim_window_drawto(wavewin, x, hy);
}

/*
 * Routine for tracking the selection of signal names in the waveform window.
 */
BOOLEAN sim_window_seleachdown(INTBIG x, INTBIG y)
{
	REGISTER WINDOWPART *wavewin;
	INTBIG lowy, highy, ind, i, redo, ypos, temp_trace_spacing;
	REGISTER DISCHANNEL *tr;

	/* get the waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return(FALSE);

	sim_window_scaletowindow(wavewin, &x, &y);

	/* undraw former selection line */
	sim_window_drawtraceselect(wavewin, sim_window_currentdragy, sim_window_initialy, FALSE);
	if (y < sim_window_initialy)
	{
		lowy = y;    highy = sim_window_initialy;
	} else
	{
		lowy = sim_window_initialy;    highy = y;
	}

	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
		tr->flags &= ~TRACEWANTSELECT;
	temp_trace_spacing = 540 / sim_window_vislines;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		ind = tr->position - sim_window_topline;
		ypos = (sim_window_vislines - 1 - ind) * temp_trace_spacing + 31 + tr->nameoff;
		if (ypos > highy || ypos + sim_window_txthei < lowy) continue;
		tr->flags |= TRACEWANTSELECT;
	}

	/* see if what is highlighted has changed */
	redo = 0;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		for(i=0; i<sim_window_highlightedtracecount; i++)
			if (sim_window_highlightedtraces[i] == tr) break;
		if (i < sim_window_highlightedtracecount)
		{
			/* this signal was highlighted */
			if ((tr->flags&TRACEWANTSELECT) == 0) redo++;
		} else
		{
			/* this signal was not highlighted */
			if ((tr->flags&TRACEWANTSELECT) != 0) redo++;
		}
	}

	/* redo highlighting if it changed */
	if (redo != 0)
	{
		sim_window_cleartracehighlight();
		for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
			if ((tr->flags&TRACEWANTSELECT) != 0) sim_window_addhighlighttrace((INTBIG)tr);
	}

	/* show what is dragged out */
	sim_window_currentdragy = y;
	sim_window_drawtraceselect(wavewin, sim_window_currentdragy, sim_window_initialy, TRUE);
	return(FALSE);
}

/*
 * Routine called when vertical slider parts are clicked.
 */
BOOLEAN sim_window_verticalslider(INTBIG x, INTBIG y)
{
	INTBIG newtopline;

	if (x >= sim_waveformwindow->uselx) return(FALSE);
	switch (sim_waveformsliderpart)
	{
		case 0: /* down arrow clicked */
			if (y > sim_waveformwindow->usely + DISPLAYSLIDERSIZE) return(FALSE);
			if (y < sim_waveformwindow->usely) return(FALSE);
			if (sim_window_topline + sim_window_vislines < sim_window_lines)
			{
				sim_window_topline++;
				sim_window_redisphandler(sim_waveformwindow);
			}
			break;
		case 1: /* below thumb */
			if (y > sim_waveformwindow->thumbly) return(FALSE);
			newtopline = sim_window_topline + sim_window_vislines - 1;
			if (newtopline + sim_window_vislines > sim_window_lines)
				newtopline = sim_window_lines - sim_window_vislines;
			if (sim_window_topline == newtopline) break;
			sim_window_topline = newtopline;
			sim_window_redisphandler(sim_waveformwindow);
			break;
		case 2: /* on thumb (not handled here) */
			break;
		case 3: /* above thumb */
			if (y < sim_waveformwindow->thumbhy) return(FALSE);
			if (sim_window_topline == 0) return(FALSE);
			sim_window_topline -= sim_window_vislines - 1;
			if (sim_window_topline < 0) sim_window_topline = 0;
			sim_window_redisphandler(sim_waveformwindow);
			break;
		case 4: /* up arrow clicked */
			if (y < sim_waveformwindow->usehy - DISPLAYSLIDERSIZE) return(FALSE);
			if (sim_window_topline > 0)
			{
				sim_window_topline--;
				sim_window_redisphandler(sim_waveformwindow);
			}
			break;
	}
	return(FALSE);
}

/*
 * Routine called when horizontal slider parts are clicked.
 */
BOOLEAN sim_window_horizontalslider(INTBIG x, INTBIG y)
{
	double range;

	if (y >= sim_waveformwindow->usely) return(FALSE);
	switch (sim_waveformsliderpart)
	{
		case 0: /* left arrow clicked */
			if (x > sim_waveformwindow->uselx + DISPLAYSLIDERSIZE) return(FALSE);
			if (sim_window_mintime <= 0.0) break;
			range = sim_window_maxtime - sim_window_mintime;
			sim_window_mintime -= range/DISPLAYSMALLSHIFT;
			sim_window_maxtime -= range/DISPLAYSMALLSHIFT;
			if (sim_window_mintime < 0.0)
			{
				sim_window_maxtime -= sim_window_mintime;
				sim_window_mintime = 0.0;
			}
			if (sim_window_format == ALS) (void)simals_initialize_simulator(TRUE); else
				sim_window_redraw();
			break;
		case 1: /* left of thumb */
			if (x > sim_waveformwindow->thumblx) return(FALSE);
			if (sim_window_mintime <= 0.0) break;
			range = sim_window_maxtime - sim_window_mintime;
			sim_window_mintime -= range/DISPLAYLARGESHIFT;
			sim_window_maxtime -= range/DISPLAYLARGESHIFT;
			if (sim_window_mintime < 0.0)
			{
				sim_window_maxtime -= sim_window_mintime;
				sim_window_mintime = 0.0;
			}
			if (sim_window_format == ALS) (void)simals_initialize_simulator(TRUE); else
				sim_window_redraw();
			break;
		case 2: /* on thumb (not handled here) */
			break;
		case 3: /* right of thumb */
			if (x < sim_waveformwindow->thumbhx) return(FALSE);
			range = sim_window_maxtime - sim_window_mintime;
			sim_window_mintime += range/DISPLAYLARGESHIFT;
			sim_window_maxtime += range/DISPLAYLARGESHIFT;
			if (sim_window_format == ALS) (void)simals_initialize_simulator(TRUE); else
				sim_window_redraw();
			break;
		case 4: /* right arrow clicked */
			if (x < sim_waveformwindow->usehx - DISPLAYSLIDERSIZE) return(FALSE);
			range = sim_window_maxtime - sim_window_mintime;
			sim_window_mintime += range/DISPLAYSMALLSHIFT;
			sim_window_maxtime += range/DISPLAYSMALLSHIFT;
			if (sim_window_format == ALS) (void)simals_initialize_simulator(TRUE); else
				sim_window_redraw();
			break;
	}
	return(FALSE);
}

void sim_window_buttonhandler(WINDOWPART *w, INTBIG button, INTBIG inx, INTBIG iny)
{
	INTBIG x, y, xpos, ypos, j, ind, temp_trace_spacing, tracelinesmoved;
	char *par[2];
	REGISTER DISCHANNEL *tr, *otr, *otrprev, *nexttr, *addloc,
		*buschain, *endbuschain;

	/* changes to the mouse-wheel are handled by the user interface */
	if (wheelbutton(button))
	{
		us_buttonhandler(w, button, inx, iny);
		return;
	}
	ttynewcommand();

	/* hit in slider on left */
	if (inx < w->uselx)
	{
		if (iny <= w->usely + DISPLAYSLIDERSIZE)
		{
			/* hit in bottom arrow: go down 1 (may repeat) */
			sim_waveformwindow = w;
			sim_waveformsliderpart = 0;
			trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_verticalslider, us_nullchar,
				us_nullvoid, TRACKNORMAL);
			return;
		}
		if (iny < w->thumbly)
		{
			/* hit below thumb: go down lots (may repeat) */
			sim_waveformwindow = w;
			sim_waveformsliderpart = 1;
			trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_verticalslider, us_nullchar,
				us_nullvoid, TRACKNORMAL);
			return;
		}
		if (iny <= w->thumbhy)
		{
			/* hit on thumb: track thumb motion */
			sim_waveformwindow = w;
			sim_window_deltasofar = 0;
			sim_window_initialthumb = w->thumbhy;
			us_vthumbbegin(iny, w, w->uselx-DISPLAYSLIDERSIZE, w->usely, w->usehy,
				TRUE, sim_window_vthumbtrackingcallback);
			trackcursor(FALSE, us_nullup, us_nullvoid, us_vthumbdown, us_nullchar,
				us_vthumbdone, TRACKNORMAL); 
			return;
		}
		if (iny < w->usehy - DISPLAYSLIDERSIZE)
		{
			/* hit above thumb: go up lots (may repeat) */
			sim_waveformwindow = w;
			sim_waveformsliderpart = 3;
			trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_verticalslider, us_nullchar,
				us_nullvoid, TRACKNORMAL);
			return;
		}

		/* hit in up arrow: go up 1 (may repeat) */
		sim_waveformwindow = w;
		sim_waveformsliderpart = 4;
		trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_verticalslider, us_nullchar,
			us_nullvoid, TRACKNORMAL);
		return;
	}

	/* hit in slider on bottom */
	if (iny < w->usely)
	{
		if (inx <= w->uselx + DISPLAYSLIDERSIZE)
		{
			/* hit in left arrow: small shift down in time (may repeat) */
			sim_waveformwindow = w;
			sim_waveformsliderpart = 0;
			trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_horizontalslider, us_nullchar,
				us_nullvoid, TRACKNORMAL);
			return;
		}
		if (inx < w->thumblx)
		{
			/* hit to left of thumb: large shift down in time (may repeat) */
			sim_waveformwindow = w;
			sim_waveformsliderpart = 1;
			trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_horizontalslider, us_nullchar,
				us_nullvoid, TRACKNORMAL);
			return;
		}
		if (inx <= w->thumbhx)
		{
			/* hit on thumb: track thumb motion */
			sim_waveformwindow = w;
			us_hthumbbegin(inx, w, w->usely, w->uselx, w->usehx,
				sim_window_hthumbtrackingcallback);
			trackcursor(FALSE, us_nullup, us_nullvoid, us_hthumbdown, us_nullchar,
				us_hthumbdone, TRACKNORMAL); 
			return;
		}
		if (inx < w->usehx - DISPLAYSLIDERSIZE)
		{
			/* hit to right of thumb: large shift up in time (may repeat) */
			sim_waveformwindow = w;
			sim_waveformsliderpart = 3;
			trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_horizontalslider, us_nullchar,
				us_nullvoid, TRACKNORMAL);
			return;
		}

		/* hit in right arrow: small shift up in time (may repeat) */
		sim_waveformwindow = w;
		sim_waveformsliderpart = 4;
		trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_horizontalslider, us_nullchar,
			us_nullvoid, TRACKNORMAL);
		return;
	}

	sim_window_setviewport(w);
	x = inx;   y = iny;
	sim_window_scaletowindow(w, &x, &y);

	/* see if a signal name was hit */
	if (x < 114)
	{
		temp_trace_spacing = 540 / sim_window_vislines;
		ind = sim_window_vislines - 1 - (y - 31) / temp_trace_spacing;
		for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
		{
			if ((tr->flags&TRACENOTDRAWN) == 0)
			{
				if (tr->position == ind + sim_window_topline)
				{
					ypos = (sim_window_vislines - 1 - ind) * temp_trace_spacing + 31 + tr->nameoff;
					if (y >= ypos && y <= ypos + sim_window_txthei) break;
				}
			}
		}
		if (tr == NODISCHANNEL)
		{
			/* not over any signal: drag out selection of signal names */
			sim_window_initialx = x;
			sim_window_initialy = y;
			sim_window_currentdragy = y;
			trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_seleachdown,
				us_nullchar, us_nullvoid, TRACKDRAGGING);
			sim_window_drawtraceselect(w, sim_window_currentdragy, sim_window_initialy, FALSE);
			return;
		}

		/* see if it is a double-click on a bus name */
		if (doublebutton(button))
		{
			if ((tr->flags&TRACETYPE) == TRACEISBUS)
			{
				/* closed bus: open it up */
				j = 0;
				for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
				{
					if (otr->buschannel != tr) continue;
					otr->flags &= ~TRACENOTDRAWN;
					otr->position = --j;
				}
				tr->flags = (tr->flags & ~TRACETYPE) | TRACEISOPENBUS;

				/* renumber lines in the display */
				sim_window_renumberlines();

				/* adjust the number of visible lines */
				sim_window_setviewport(w);

				sim_window_redraw();
				return;
			}
			if ((tr->flags&TRACETYPE) == TRACEISOPENBUS)
			{
				/* opened bus: close it up */
				for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
					if (otr->buschannel == tr) otr->flags |= TRACENOTDRAWN;
				tr->flags = (tr->flags & ~TRACETYPE) | TRACEISBUS;

				/* renumber lines in the display */
				sim_window_renumberlines();

				sim_window_redraw();
				return;
			}
			return;
		}

		/* see if the signal shares a line with any others */
		for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
		{
			if ((otr->flags&TRACENOTDRAWN) != 0) continue;
			if (otr != tr && otr->position == tr->position) break;
		}
		if (otr == NODISCHANNEL)
		{
			/* not a shared signal: handle dragging of the name */
			sim_window_tracedragyoff = 0;
			sim_window_initialy = y;
			sim_window_tracedragtr = tr;
			sim_window_drawtracedrag(w, TRUE);
			trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_eachdown,
				us_nullchar, us_nullvoid, TRACKDRAGGING);
			sim_window_drawtracedrag(w, FALSE);

			/* see if the signal name was dragged to a new location */
			tracelinesmoved = sim_window_tracedragyoff / temp_trace_spacing;
			if (tracelinesmoved != 0)
			{
				/* erase highlighting */
				sim_window_cleartracehighlight();

				/* find which signal name it was dragged to */
				otrprev = NODISCHANNEL;
				for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
				{
					if ((otr->flags&TRACENOTDRAWN) == 0)
					{
						if (otr->position == tr->position - tracelinesmoved) break;
					}
					if (otr != tr) otrprev = otr;
				}
				if (otr != NODISCHANNEL)
				{
					/* pull out the selected signal */
					sim_window_removetrace(tr);

					/* insert signal in new location */
					if (tr->position < otr->position)
					{
						sim_window_addtrace(tr, otr);
					} else
					{
						sim_window_addtrace(tr, otrprev);
					}

					/* move any bus signals inside of this */
					buschain = endbuschain = NODISCHANNEL;
					for(otr = sim_window_plot; otr != NODISCHANNEL; otr = nexttr)
					{
						nexttr = otr->nextdischannel;
						if (otr->buschannel == tr)
						{
							sim_window_removetrace(otr);
							if (endbuschain == NODISCHANNEL) endbuschain = buschain = otr; else
							{
								endbuschain->nextdischannel = otr;
								endbuschain = otr;
							}
							otr->nextdischannel = NODISCHANNEL;
						}
					}
					addloc = tr;
					for(otr = buschain; otr != NODISCHANNEL; otr = nexttr)
					{
						nexttr = otr->nextdischannel;
						sim_window_addtrace(otr, addloc);
						addloc = otr;
					}

					/* renumber lines in the display */
					sim_window_renumberlines();

					/* redisplay window */
					sim_window_redraw();

					/* save the new configuration */
					sim_window_savesignalorder();
				}
				sim_window_addhighlighttrace((INTBIG)tr);
				par[0] = "highlight";
				telltool(net_tool, 1, par);
				return;
			}
		}

		/* highlight the selected signal */
		if (!shiftbutton(button))
		{
			/* shift not held: simply highlight this signal */
			sim_window_cleartracehighlight();
			sim_window_addhighlighttrace((INTBIG)tr);
		} else
		{
			/* complement state of this signal */
			for(j=0; j<sim_window_highlightedtracecount; j++)
				if (tr == sim_window_highlightedtraces[j]) break;
			if (j < sim_window_highlightedtracecount)
			{
				/* signal already highlighted: unhighlight it */
				sim_window_deletehighlighttrace((INTBIG)tr);
			} else
			{
				/* signal new: add to highlight */
				sim_window_addhighlighttrace((INTBIG)tr);
			}

		}
		par[0] = "highlight";
		telltool(net_tool, 1, par);
		return;
	}

	/* see if a cursor was hit */
	xpos = sim_window_timetoxpos(sim_window_maincursor);
	if (abs(x-xpos) < 10)
	{
		/* main cursor hit */
		trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_eachwdown,
			us_nullchar, us_nullvoid, TRACKDRAGGING);
		return;
	}
	xpos = sim_window_timetoxpos(sim_window_extensioncursor);
	if (abs(x-xpos) < 10)
	{
		/* extension cursor hit */
		trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_eachbdown,
			us_nullchar, us_nullvoid, TRACKDRAGGING);
		return;
	}
}

/*
 * This procedure redraws the display, allowing for resize.
 */
void sim_window_redisphandler(WINDOWPART *w)
{
	INTBIG lx, hx, ly, hy;
	UINTBIG descript[TEXTDESCRIPTSIZE];
	double range;
	REGISTER INTBIG i, thumbtop, thumbarea, thumbsize;

	sim_window_setviewport(w);

	/* clear the window */
	sim_window_desc.bits = LAYERA;
	sim_window_desc.col = ALLOFF;
	w->uselx -= DISPLAYSLIDERSIZE;
	w->usely -= DISPLAYSLIDERSIZE;
	lx = w->uselx;   hx = w->usehx;
	ly = w->usely;   hy = w->usehy;
	screendrawbox(w, lx, hx, ly, hy, &sim_window_desc);

	/* draw red outline */
	if ((w->state&WINDOWSIMULATING) != 0)
	{
		sim_window_desc.col = RED;
		screendrawbox(w, lx-SIMULATINGBORDERSIZE, hx+SIMULATINGBORDERSIZE,
			ly-SIMULATINGBORDERSIZE, ly-1, &sim_window_desc);
		screendrawbox(w, lx-SIMULATINGBORDERSIZE, hx+SIMULATINGBORDERSIZE,
			hy+1, hy+SIMULATINGBORDERSIZE, &sim_window_desc);
		screendrawbox(w, lx-SIMULATINGBORDERSIZE, lx-1, ly, hy, &sim_window_desc);
		screendrawbox(w, hx+1, hx+SIMULATINGBORDERSIZE, ly, hy, &sim_window_desc);
	}

	/* draw vertical slider on the left */
	us_drawverticalslider(w, lx-2, ly+DISPLAYSLIDERSIZE, hy, TRUE);
	if (sim_window_lines > 0)
	{
		thumbsize = sim_window_vislines * 100 / sim_window_lines;
		if (thumbsize >= 100) thumbsize = 100;
		if (thumbsize == 100 && sim_window_topline == 0)
		{
			w->thumbly = 0;   w->thumbhy = -1;
		} else
		{
			thumbtop = sim_window_topline * 100 / sim_window_lines;
			thumbarea = w->usehy - w->usely - DISPLAYSLIDERSIZE*2;

			w->thumbhy = w->usehy - DISPLAYSLIDERSIZE - thumbarea * thumbtop / 100;
			w->thumbly = w->thumbhy - thumbarea * thumbsize / 100;
			if (w->thumbhy > w->usehy-DISPLAYSLIDERSIZE-2) w->thumbhy = w->usehy-DISPLAYSLIDERSIZE-2;
			if (w->thumbly < w->usely+DISPLAYSLIDERSIZE*2+2) w->thumbly = w->usely+DISPLAYSLIDERSIZE*2+2;
		}
		us_drawverticalsliderthumb(w, lx-2, ly+DISPLAYSLIDERSIZE, hy, w->thumbly, w->thumbhy);
	}

	/* draw horizontal slider on the bottom */
	us_drawhorizontalslider(w, ly+DISPLAYSLIDERSIZE, lx+DISPLAYSLIDERSIZE, hx);
	thumbarea = w->usehx - w->uselx - DISPLAYSLIDERSIZE*2;
	if (sim_window_mintime <= 0.0)
	{
		w->thumblx = w->uselx+DISPLAYSLIDERSIZE*2+2;
		w->thumbhx = w->thumblx + thumbarea/2;
	} else
	{
		range = sim_window_maxtime * 2.0;
		w->thumblx = (INTBIG)(w->uselx+DISPLAYSLIDERSIZE*2+2 + sim_window_mintime/range*thumbarea);
		w->thumbhx = (INTBIG)(w->uselx+DISPLAYSLIDERSIZE*2+2 + sim_window_maxtime/range*thumbarea);
	}
	if (w->thumbhx > w->usehx-DISPLAYSLIDERSIZE-2) w->thumbhx = w->usehx-DISPLAYSLIDERSIZE-2;
	if (w->thumblx < w->uselx+DISPLAYSLIDERSIZE*2+2) w->thumblx = w->uselx+DISPLAYSLIDERSIZE*2+2;
	us_drawhorizontalsliderthumb(w, ly+DISPLAYSLIDERSIZE, lx+DISPLAYSLIDERSIZE, hx, w->thumblx, w->thumbhx);

	us_drawslidercorner(w, lx, lx+DISPLAYSLIDERSIZE-1, ly, ly+DISPLAYSLIDERSIZE-1);

	/* reset bounds */
	w->uselx += DISPLAYSLIDERSIZE;
	w->usely += DISPLAYSLIDERSIZE;

	/* write banner at top of window */
	TDCLEAR(descript);
	TDSETSIZE(descript, TXTSETPOINTS(12));
	screensettextinfo(w, NOTECHNOLOGY, descript);
	sim_window_desc.col = el_colmentxt;
	(void)initinfstr();
	if ((w->state&WINDOWSIMULATING) == 0)
		(void)addstringtoinfstr(_("Inactive Simulation Window, ? for help")); else
			(void)addstringtoinfstr(_("Simulation Window, ? for help"));
	lx = w->uselx;
	screendrawtext(w, lx+5, hy-18, returninfstr(), &sim_window_desc);
	screeninvertbox(w, lx, hx, hy-20, hy);

	sim_window_drawtimescale(w);
	sim_window_drawgraph(w);
	sim_window_drawcursors(w);
	for(i=0; i<sim_window_highlightedtracecount; i++)
		sim_window_plottrace(sim_window_highlightedtraces[i], w, 1);
}

/****************************** SUPPORT FUNCTIONS ******************************/

/*
 * Routine to convert from simulation time to the proper X position in the waveform window.
 */
INTBIG sim_window_timetoxpos(double time)
{
	double result;

	result = 653.0 * (time - sim_window_mintime) / (sim_window_maxtime - sim_window_mintime) + 114.0;
	return(rounddouble(result));
}

/*
 * Routine to convert from the X position in the waveform window to simulation time.
 */
double sim_window_xpostotime(INTBIG xpos)
{
	double result;

	result = (xpos-114.0) * (sim_window_maxtime-sim_window_mintime) / 653.0 + sim_window_mintime;
	return(result);
}

/*
 * This procedure prints out the time scale on the bottom of the waveform window.
 */
void sim_window_drawtimescale(WINDOWPART *wavewin)
{
	INTBIG x_pos;
	char s2[20];
#if 1
	double time, separation;

	sim_window_setmask(LAYERA);
	sim_window_setcolor(MENTXT);
	separation = sim_window_sensibletimevalue((sim_window_maxtime - sim_window_mintime) / 5.0);
	time = sim_window_sensibletimevalue(sim_window_maxtime);
	while (time + separation < sim_window_maxtime)
		time += separation;
	for(;;)
	{
		if (time < sim_window_mintime) break;
		x_pos = sim_window_timetoxpos(time);
		sim_windowconvertengineeringnotation(time, s2);
		sim_window_moveto(x_pos, 0);
		sim_window_drawcstring(wavewin, s2);
		time -= separation;
	}
#else
	INTBIG i;
	double time;

	sim_window_setmask(LAYERA);
	sim_window_setcolor(MENTXT);
	for(i=1; i<5; i++)
	{
		time = i * 0.2 * (sim_window_maxtime - sim_window_mintime) + sim_window_mintime;
		x_pos = sim_window_timetoxpos(time);
		sim_windowconvertengineeringnotation(time, s2);
		sim_window_moveto(x_pos, 0);
		sim_window_drawcstring(wavewin, s2);
	}
#endif
}

/*
 * Routine to truncate the time value "time" to the next lower value that is "integral"
 */
double sim_window_sensibletimevalue(double time)
{
	double scale;

	if (time != 0.0)
	{
		scale = 1.0;
		while (doubleslessthan(time, 1.0))
		{
			time *= 10.0;
			scale /= 10.0;
		}
		time = doublefloor(time) * scale;
	}
	return(time);
}

void sim_window_writetracename(WINDOWPART *wavewin, DISCHANNEL *tr)
{
	INTBIG y_pos, temp_trace_spacing, line, px, py, maxheight, maxwidth, len, save, size;
	UINTBIG descript[TEXTDESCRIPTSIZE];
	INTBIG wid, hei;

	/* compute positions */
	temp_trace_spacing = 540 / sim_window_vislines;
	line = tr->position - sim_window_topline;
	if (line < 0 || line >= sim_window_vislines) return;
	y_pos = (sim_window_vislines - 1 - line) * temp_trace_spacing + 31;

	/* draw the vertical line between the label and the trace */
	sim_window_setcolor(MENGLY);
	sim_window_moveto(113, y_pos);
	sim_window_drawto(wavewin, 113, y_pos+temp_trace_spacing-10);

	/* set the text color */
	if ((tr->flags&TRACETYPE) == TRACEISANALOG) sim_window_setcolor(tr->color); else
		sim_window_setcolor(MENTXT);

	/* write the text */
	if (tr->buschannel == NODISCHANNEL) px = 1; else px = 10;
	py = y_pos + tr->nameoff;
	maxheight = applyyscale(wavewin, temp_trace_spacing);
	maxwidth = applyxscale(wavewin, 112-px);
	sim_window_mapcoord(wavewin, &px, &py);
	len = strlen(tr->name);
	TDCLEAR(descript);
	for(size = sim_window_textsize; size >= 4; size--)
	{
		TDSETSIZE(descript, TXTSETPOINTS(size));
		screensettextinfo(wavewin, NOTECHNOLOGY, descript);
		screengettextsize(wavewin, tr->name, &wid, &hei);
		if (hei > maxheight && size > 4) continue;
		sim_window_txthei = hei * (wavewin->screenhy - wavewin->screenly) /
			(wavewin->usehy - wavewin->usely - 20);
		while (len > 1 && wid > maxwidth)
		{
			len--;
			save = tr->name[len];   tr->name[len] = 0;
			screengettextsize(wavewin, tr->name, &wid, &hei);
			tr->name[len] = (char)save;
		}
		save = tr->name[len];   tr->name[len] = 0;
		screendrawtext(wavewin, px, py, tr->name, &sim_window_desc);
		tr->name[len] = (char)save;
		break;
	}
}

/*
 * This procedure draws the cursors on the timing diagram.  The other color
 * planes are write protected so that the cursor doesn't alter any of the
 * timing diagram information.
 */
void sim_window_drawcursors(WINDOWPART *wavewin)
{
	INTBIG x_pos;
	char s2[40];
	double v;

	/* erase all cursors */
	sim_window_setcolor(ALLOFF);
	sim_window_setmask(LAYERH);
	sim_window_drawbox(wavewin, 114, 0, 767, 570);
	sim_window_setcolor(HIGHLIT);

	/* draw main cursor */
	x_pos = sim_window_timetoxpos(sim_window_maincursor);
	if (x_pos >= 114 && x_pos <= 767)
	{
		sim_window_moveto(x_pos, 31);
		sim_window_drawto(wavewin, x_pos, 570);
	}

	/* draw extension cursor */
	x_pos = sim_window_timetoxpos(sim_window_extensioncursor);
	if (x_pos >= 114 && x_pos <= 767)
	{
		sim_window_moveto(x_pos, 31);
		sim_window_drawto(wavewin, x_pos, 570);
		sim_window_moveto(x_pos-5, 558);
		sim_window_drawto(wavewin, x_pos+5, 568);
		sim_window_moveto(x_pos+5, 558);
		sim_window_drawto(wavewin, x_pos-5, 568);
	}

	/* draw cursor locations textually */
	sim_window_setmask(LAYERA);
	sim_window_setcolor(ALLOFF);
	sim_window_drawbox(wavewin, 114, 571, 767, 600);		/* cursor text area */
	sim_window_setcolor(MENTXT);
	sim_window_moveto(114, 600);
	strcpy(s2, _("Main: "));
	sim_windowconvertengineeringnotation(sim_window_maincursor, &s2[6]);
	sim_window_drawulstring(wavewin, s2);
	sim_window_moveto(333, 600);
	strcpy(s2, _("Ext: "));
	sim_windowconvertengineeringnotation(sim_window_extensioncursor, &s2[5]);
	sim_window_drawulstring(wavewin, s2);
	sim_window_moveto(552, 600);
	strcpy(s2, _("Delta: "));
	v = sim_window_extensioncursor - sim_window_maincursor;
	if (v < 0.0) v = -v;
	sim_windowconvertengineeringnotation(v, &s2[7]);
	sim_window_drawulstring(wavewin, s2);
}

void sim_window_drawgraph(WINDOWPART *wavewin)
{
	INTBIG y_min, y_max, j, maxoff, slot, temp_trace_spacing, range, hasana;
	UINTBIG descript[TEXTDESCRIPTSIZE];
	INTBIG x, y;
	char s2[15];
	REGISTER DISCHANNEL *tr, *otr;

	/* determine height of text in the window */
	TDCLEAR(descript);
	TDSETSIZE(descript, TXTSETPOINTS(sim_window_textsize));
	screensettextinfo(wavewin, NOTECHNOLOGY, descript);
	screengettextsize(wavewin, "Xy", &x, &y);
	sim_window_txthei = y * (wavewin->screenhy - wavewin->screenly) /
		(wavewin->usehy - wavewin->usely - 20);

	/* set name offsets */
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel) tr->nameoff = -1;
	temp_trace_spacing = 540 / sim_window_vislines;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		/* determine position of trace label on the line */
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		if (tr->nameoff < 0)
		{
			hasana = 0;
			maxoff = 0;
			for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
			{
				if ((otr->flags&TRACENOTDRAWN) != 0) continue;
				if (otr->position != tr->position) continue;
				maxoff++;
				if ((otr->flags&TRACETYPE) == TRACEISANALOG) hasana++;
			}
			if (maxoff == 1)
			{
				/* only one trace: center it */
				tr->nameoff = (temp_trace_spacing - 10 - sim_window_txthei) / 2;
				if (tr->nameoff < 0) tr->nameoff = 0;
			} else
			{
				/* multiple traces: compute them */
				range = (temp_trace_spacing - 10 - sim_window_txthei*3) / (maxoff-1);
				if (range <= 0) range = 1;
				slot = 0;
				for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
				{
					if (otr->position != tr->position) continue;
					otr->nameoff = slot * range + sim_window_txthei;
					slot++;
				}
			}

			/* if there are analog traces on this line, show height values */
			if (hasana != 0)
			{
				y_min = (sim_window_vislines - 1 - (tr->position - sim_window_topline)) * temp_trace_spacing + 31;
				y_max = y_min + temp_trace_spacing - 10;
				sim_window_setmask(LAYERA);
				sim_window_setcolor(MENTXT);
				sim_window_moveto(100, y_min);
				sim_window_drawto(wavewin, 113, y_min);
				sim_window_moveto(112, y_min);
				(void)sprintf(s2, "%g", sim_window_anadislow);
				sim_window_drawrstring(wavewin, s2);

				sim_window_moveto(100, y_max);
				sim_window_drawto(wavewin, 113, y_max);
				sim_window_moveto(112, y_max-sim_window_txthei);
				(void)sprintf(s2, "%g", sim_window_anadishigh);
				sim_window_drawrstring(wavewin, s2);
			}
		}
	}

	/* set colors for analog traces */
	j = 0;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;

		/* handle analog trace color */
		if ((tr->flags&TRACETYPE) == TRACEISANALOG)
		{
			tr->color = sim_window_anacolor[j];
			j++;
			if (sim_window_anacolor[j] == 0) j = 0;
		}
	}

	/* plot it all */
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		if (stopping(STOPREASONDISPLAY)) break;
		sim_window_plottrace(tr, wavewin, 0);
	}
}

/*
 * Routine to draw a trace "tr" in waveform window "wavewin".  "style" is:
 *  0   draw normal trace
 *  1   highlight trace
 *  2   unhighlight trace
 */
void sim_window_plottrace(DISCHANNEL *tr, WINDOWPART *wavewin, INTBIG style)
{
	INTBIG x_min, x_max, y_min, y_max, j, state, laststate, first,
		slot, temp_trace_spacing, fx, fy, tx, ty, color, lx, hx, ly, hy, strength,
		cfx, cfy, ctx, cty, sigcount, newtotal, flags;
	char s2[50];
	REGISTER DISCHANNEL *bustr;
	double time, position, nexttime;

	/* get coordinates */
	temp_trace_spacing = 540 / sim_window_vislines;
	slot = tr->position - sim_window_topline;
	if (slot < 0 || slot >= sim_window_vislines) return;
	y_min = (sim_window_vislines - 1 - slot) * temp_trace_spacing + 31;
	y_max = y_min + temp_trace_spacing - 10;

	/* handle name field on the left */
	if (style == 0)
	{
		/* draw normal trace name */
		sim_window_writetracename(wavewin, tr);
	} else
	{
		/* highlight/unhighlight trace name */
		ly = y_min + tr->nameoff+1;   hy = ly + sim_window_txthei;
		lx = 0;                       hx = 112;
		sim_window_setmask(LAYERH);
		if (style == 1) color = HIGHLIT; else color = 0;
		sim_window_setcolor(color);
		sim_window_moveto(lx, ly);
		sim_window_drawto(wavewin, hx, ly);
		sim_window_drawto(wavewin, hx, hy);
		sim_window_drawto(wavewin, lx, hy);
		sim_window_drawto(wavewin, lx, ly);
	}

	/* handle busses */
	flags = tr->flags & TRACETYPE;
	if (flags == TRACEISBUS || flags == TRACEISOPENBUS)
	{
		/* find starting time and enumerate signals */
		sigcount = 0;
		for(bustr = sim_window_plot; bustr != NODISCHANNEL; bustr = bustr->nextdischannel)
		{
			if (bustr->buschannel != tr) continue;

			/* LINTED "time" used in proper order */
			if (sigcount == 0 || bustr->timearray[0] < time)
				time = bustr->timearray[0];
			bustr->busindex = sigcount++;
		}

		/* make sure there is room for all of the signal values */
		if (sigcount > sim_window_sigtotal)
		{
			newtotal = sim_window_sigtotal * 2;
			if (newtotal == 0) newtotal = 8;
			if (newtotal < sigcount) newtotal = sigcount;
			if (sim_window_sigtotal > 0) efree((char *)sim_window_sigvalues);
			sim_window_sigtotal = 0;
			sim_window_sigvalues = (INTBIG *)emalloc(newtotal * SIZEOFINTBIG, sim_tool->cluster);
			if (sim_window_sigvalues == 0) return;
			sim_window_sigtotal = newtotal;
		}

		/* draw the waveform */
		x_min = 114;
		for(;;)
		{
			/* compute signal value at time "time" */
			for(bustr = sim_window_plot; bustr != NODISCHANNEL; bustr = bustr->nextdischannel)
			{
				if (bustr->buschannel != tr) continue;
				for(j=0; j<bustr->numsteps; j++)
					if (time < bustr->timearray[j]) break;
				if (j > 0) j--;
				sim_window_sigvalues[bustr->busindex] = bustr->statearray[j] >> 8;
			}

			/* compute the value string */
			strcpy(s2, sim_window_makewidevalue(sigcount, sim_window_sigvalues));

			/* draw the value */
			x_max = sim_window_timetoxpos(time);
			if (x_max < 114) x_max = 114;
			if (x_max > 767) break;
			if (x_max > x_min)
			{
				if (style == 0)
					sim_window_setcolor(sim_colorstrengthpower);
				sim_window_moveto(x_min, (y_min+y_max)/2);
				sim_window_drawto(wavewin, x_min+BUSANGLE, y_min);
				sim_window_drawto(wavewin, x_max-BUSANGLE, y_min);
				sim_window_drawto(wavewin, x_max, (y_min+y_max)/2);
				sim_window_drawto(wavewin, x_max-BUSANGLE, y_max);
				sim_window_drawto(wavewin, x_min+BUSANGLE, y_max);
				sim_window_drawto(wavewin, x_min, (y_min+y_max)/2);
				if (style == 0)
				{
					sim_window_moveto(x_max+BUSANGLE/2, (y_min+y_max)/2);
					sim_window_drawhcstring(wavewin, s2);
				}
				x_min = x_max;
			}

			/* advance to the next time */
			nexttime = time;
			for(bustr = sim_window_plot; bustr != NODISCHANNEL; bustr = bustr->nextdischannel)
			{
				if (bustr->buschannel != tr) continue;
				for(j=0; j<bustr->numsteps; j++)
					if (bustr->timearray[j] > time) break;
				if (j < bustr->numsteps)
				{
					if (nexttime == time || bustr->timearray[j] < nexttime)
						nexttime = bustr->timearray[j];
				}
			}
			if (nexttime == time) break;
			time = nexttime;
		}
		if (x_max < 767)
		{
			x_min = x_max;   x_max = 767;
			if (style == 0)
				sim_window_setcolor(sim_colorstrengthpower);
			sim_window_moveto(x_min, (y_min+y_max)/2);
			sim_window_drawto(wavewin, x_min+BUSANGLE, y_min);
			sim_window_drawto(wavewin, x_max-BUSANGLE, y_min);
			sim_window_drawto(wavewin, x_max, (y_min+y_max)/2);
			sim_window_drawto(wavewin, x_max-BUSANGLE, y_max);
			sim_window_drawto(wavewin, x_min+BUSANGLE, y_max);
			sim_window_drawto(wavewin, x_min, (y_min+y_max)/2);
		}
		if (style != 0) sim_window_setmask(LAYERA);
		return;
	}

	/* handle digital waveform */
	if (flags == TRACEISDIGITAL)
	{
		first = 1;
		for(j=0; j<tr->numsteps; j++)
		{
			time = tr->timearray[j];
			if (j == tr->numsteps-1) x_max = 767; else
			{
				x_max = sim_window_timetoxpos(tr->timearray[j+1]);
				if (x_max < 114) continue;
				if (x_max > 767) x_max = 767;
			}
			x_min = sim_window_timetoxpos(time);
			if (x_min < 114) x_min = 114;
			if (x_min > 767) continue;

			state = tr->statearray[j] >> 8;
			if (style == 0)
			{
				strength = tr->statearray[j] & 0377;
				if (strength == 0) color = sim_colorstrengthoff; else
					if (strength <= NODE_STRENGTH) color = sim_colorstrengthnode; else
						if (strength <= GATE_STRENGTH) color = sim_colorstrengthgate; else
							color = sim_colorstrengthpower;
				sim_window_setcolor(color);
			}

			/* LINTED "laststate" used in proper order */
			if (first == 0 && state != laststate)
			{
				sim_window_moveto(x_min, y_min);
				sim_window_drawto(wavewin, x_min, y_max);
			}
			first = 0;
			switch (state)
			{
				case LOGIC_LOW:
					sim_window_moveto(x_min, y_min);
					sim_window_drawto(wavewin, x_max, y_min);
					break;
				case LOGIC_X:
					sim_window_drawbox(wavewin, x_min, y_min, x_max, y_max);
					break;
				case LOGIC_HIGH:
					sim_window_moveto(x_min, y_max);
					sim_window_drawto(wavewin, x_max, y_max);
					break;
				default:
					sim_window_drawbox(wavewin, x_min, y_min, x_max, y_max);
					if (style == 0)
					{
						if ((x_max - x_min) <= 1) break;
						sim_window_setcolor(ALLOFF);
						sim_window_drawbox(wavewin, x_min+1, y_min+1, x_max-1, y_max-1);
						(void)sprintf(s2, "0x%lX", state);
						sim_window_moveto((x_min + x_max) / 2, y_min+3);
						sim_window_setcolor(color);
						sim_window_drawcstring(wavewin, s2);
					}
					break;
			}
			laststate = state;
		}
		if (style != 0) sim_window_setmask(LAYERA);
		return;
	}

	/* handle analog waveform */
	first = 1;
	for(j=0; j<tr->numsteps; j++)
	{
		time = tr->timearray[j];
		tx = sim_window_timetoxpos(time);
		position = (double)(y_max - y_min);
		position *= (tr->valuearray[j] * sim_window_anarange - sim_window_anadislow) /
			sim_window_anadisrange;
		ty = (INTBIG)position + y_min;
		if (j != 0 && tx >= 114)
		{
			if (tx > 767)
			{
				/* "to" data point off the screen: interpolate */
				/* LINTED "fx" and "fy" used in proper order */
				ty = (ty-fy) * (767-fx) / (tx-fx) + fy;
				tx = 767;
			}

			if (first != 0 && fx < 114)
			{
				/* "from" data point off the screen: interpolate */
				fy = (ty-fy) * (114-fx) / (tx-fx) + fy;
				fx = 114;
			}
			first = 0;
			if (style == 0) sim_window_setcolor(tr->color);
			if (fy >= y_min && fy <= y_max &&
				ty >= y_min && ty <= y_max)
			{
				sim_window_moveto(fx, fy);
				sim_window_drawto(wavewin, tx, ty);
			} else
			{
				cfx = fx;   cfy = fy;
				ctx = tx;   cty = ty;
				if (!clipline(&cfx, &cfy, &ctx, &cty, 114, 767, y_min, y_max))
				{
					sim_window_moveto(cfx, cfy);
					sim_window_drawto(wavewin, ctx, cty);
				}
			}
		}
		fx = tx;   fy = ty;
		if (fx >= 767) break;
	}
	if (style != 0) sim_window_setmask(LAYERA);
}

/*
 * routine to snapshot the simulation window in a facet with artwork primitives
 */
void sim_window_savegraph(void)
{
	REGISTER INTBIG x_min, x_max, y_min, y_max, j, maxoff, state, laststate, color, first,
		slot, temp_trace_spacing, range, hasana, lastcolor, fx, fy, tx, ty, strength,
		sigcount, newtotal, cx, cy, *data, maxsteps;
	UINTBIG descript[TEXTDESCRIPTSIZE];
	INTBIG x, y, busdescr[12], tx1, ty1, tx2, ty2, datapos, cfx, cfy, ctx, cty;
	char s2[15];
	REGISTER NODEPROTO *np, *plotnp;
	REGISTER NODEINST *ni;
	REGISTER VARIABLE *var;
	REGISTER WINDOWPART *wavewin;
	REGISTER DISCHANNEL *tr, *otr, *bustr;
	double time, position, separation, nexttime;

	/* get facet being saved */
	np = getcurfacet();
	if (np == NONODEPROTO) return;

	/* get waveform window */
	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return;

	/* determine height of text in the window */
	TDCLEAR(descript);
	TDSETSIZE(descript, TXTSETPOINTS(sim_window_textsize));
	screensettextinfo(wavewin, NOTECHNOLOGY, descript);
	screengettextsize(wavewin, "Xy", &x, &y);
	sim_window_txthei = y * (wavewin->screenhy - wavewin->screenly) /
		(wavewin->usehy - wavewin->usely - 20);

	/* create the plot facet */
	(void)initinfstr();
	(void)addstringtoinfstr(np->cell->cellname);
	(void)addstringtoinfstr("{sim}");
	plotnp = newnodeproto(returninfstr(), el_curlib);
	if (plotnp == NONODEPROTO) return;
	setactivity("Make Simulation Plot");

	/* determine maximum number of steps */
	maxsteps = 1;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		if (tr->numsteps > maxsteps) maxsteps = tr->numsteps;
	}

	/* make space */
	data = (INTBIG *)emalloc(maxsteps * 4 * SIZEOFINTBIG, el_tempcluster);
	if (data == 0) return;

	/* compute transformation for points in the plot area */
	tx1 = 114;   ty1 = 31;    sim_window_mapcoord(wavewin, &tx1, &ty1);
	tx2 = 767;   ty2 = 570;   sim_window_mapcoord(wavewin, &tx2, &ty2);
	sim_window_offx = (tx2 - tx1) / 2;   sim_window_basex = tx1;
	sim_window_offy = (ty2 - ty1) / 2;   sim_window_basey = ty1;

	/* write the header */
	tx1 = 383;   ty1 = 600;   sim_window_mapcoord(wavewin, &tx1, &ty1);
	ni = newnodeinst(gen_invispinprim, tx1, tx1, ty1, ty1, 0, 0, plotnp);
	if (ni == NONODEINST) { efree((char *)data);  return; }
	(void)initinfstr();
	(void)addstringtoinfstr("Simulation snapshot of facet ");
	(void)addstringtoinfstr(describenodeproto(np));
	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(), VSTRING|VDISPLAY);
	if (var != NOVARIABLE)
	{
		TDSETSIZE(var->textdescript, TXTSETPOINTS(14));
		TDSETPOS(var->textdescript, VTPOSUP);
	}
	endobjectchange((INTBIG)ni, VNODEINST);

	/* set name offsets */
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel) tr->nameoff = -1;
	temp_trace_spacing = 540 / sim_window_vislines;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;

		/* determine position of trace label on the line */
		if (tr->nameoff < 0)
		{
			hasana = 0;
			maxoff = 0;
			for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
			{
				if ((otr->flags&TRACENOTDRAWN) != 0) continue;
				if (otr->position != tr->position) continue;
				maxoff++;
				if ((otr->flags&TRACETYPE) == TRACEISANALOG) hasana++;
			}
			if (maxoff == 1)
			{
				/* only one trace: center it */
				tr->nameoff = (temp_trace_spacing - 10 - sim_window_txthei) / 2;
				if (tr->nameoff < 0) tr->nameoff = 0;
			} else
			{
				/* multiple traces: compute them */
				range = (temp_trace_spacing - 10 - sim_window_txthei*3) / (maxoff-1);
				if (range <= 0) range = 1;
				slot = 0;
				for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
				{
					if ((otr->flags&TRACENOTDRAWN) != 0) continue;
					if (otr->position != tr->position) continue;
					otr->nameoff = slot * range + sim_window_txthei;
					slot++;
				}
			}

			/* if there are analog traces on this line, show height values */
			if (hasana != 0)
			{
				y_min = (sim_window_vislines - 1 - (tr->position - sim_window_topline)) * temp_trace_spacing + 31;
				y_max = y_min + temp_trace_spacing - 10;

				tx1 = 101;   ty1 = y_min;   sim_window_mapcoord(wavewin, &tx1, &ty1);
				tx2 = 113;   ty2 = y_min;   sim_window_mapcoord(wavewin, &tx2, &ty2);
				data[0] = -(tx2-tx1)/2;  data[1] = 0;
				data[2] = (tx2-tx1)/2;   data[3] = 0;
				ni = newnodeinst(art_openedpolygonprim, tx1,tx2, ty1,ty2, 0, 0, plotnp);
				if (ni == NONODEINST) { efree((char *)data);  return; }
				(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)data,
					VINTEGER|VISARRAY|(4<<VLENGTHSH));
				endobjectchange((INTBIG)ni, VNODEINST);

				(void)sprintf(s2, "%g", sim_window_anadislow);
				tx1 = 112;   ty1 = y_min;   sim_window_mapcoord(wavewin, &tx1, &ty1);
				ni = newnodeinst(gen_invispinprim, tx1, tx1, ty1, ty1, 0, 0, plotnp);
				if (ni == NONODEINST) { efree((char *)data);  return; }
				var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)s2, VSTRING|VDISPLAY);
				if (var != NOVARIABLE)
				{
					TDSETSIZE(var->textdescript, TXTSETPOINTS(10));
					TDSETPOS(var->textdescript, VTPOSUPLEFT);
				}
				endobjectchange((INTBIG)ni, VNODEINST);

				tx1 = 101;   ty1 = y_max;   sim_window_mapcoord(wavewin, &tx1, &ty1);
				tx2 = 113;   ty2 = y_max;   sim_window_mapcoord(wavewin, &tx2, &ty2);
				data[0] = -(tx2-tx1)/2;  data[1] = 0;
				data[2] = (tx2-tx1)/2;   data[3] = 0;
				ni = newnodeinst(art_openedpolygonprim, tx1,tx2, ty1,ty2, 0, 0, plotnp);
				if (ni == NONODEINST) { efree((char *)data);  return; }
				(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)data,
					VINTEGER|VISARRAY|(4<<VLENGTHSH));
				endobjectchange((INTBIG)ni, VNODEINST);

				(void)sprintf(s2, "%g", sim_window_anadishigh);
				tx1 = 112;   ty1 = y_max;   sim_window_mapcoord(wavewin, &tx1, &ty1);
				ni = newnodeinst(gen_invispinprim, tx1, tx1, ty1, ty1, 0, 0, plotnp);
				if (ni == NONODEINST) { efree((char *)data);  return; }
				var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)s2, VSTRING|VDISPLAY);
				if (var != NOVARIABLE)
				{
					TDSETSIZE(var->textdescript, TXTSETPOINTS(10));
					TDSETPOS(var->textdescript, VTPOSDOWNLEFT);
				}
				endobjectchange((INTBIG)ni, VNODEINST);
			}
		}
	}

	/* set colors for analog traces */
	j = 0;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		/* handle analog trace color */
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		if ((tr->flags&TRACETYPE) == TRACEISANALOG)
		{
			tr->color = sim_window_anacolor[j];
			j++;
			if (sim_window_anacolor[j] == 0) j = 0;
		}
	}

	/* plot time values */
	separation = sim_window_sensibletimevalue((sim_window_maxtime - sim_window_mintime) / 5.0);
	time = sim_window_sensibletimevalue(sim_window_maxtime);
	while (time + separation < sim_window_maxtime)
		time += separation;
	for(;;)
	{
		if (time < sim_window_mintime) break;
		x = sim_window_timetoxpos(time);
		sim_windowconvertengineeringnotation(time, s2);
		tx1 = x;   ty1 = 0;   sim_window_mapcoord(wavewin, &tx1, &ty1);
		ni = newnodeinst(gen_invispinprim, tx1, tx1, ty1, ty1, 0, 0, plotnp);
		if (ni == NONODEINST) { efree((char *)data);  return; }
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)s2, VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
		{
			TDSETSIZE(var->textdescript, TXTSETPOINTS(10));
			TDSETPOS(var->textdescript, VTPOSUP);
		}
		endobjectchange((INTBIG)ni, VNODEINST);
		time -= separation;
	}

	/* plot it all */
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		if (stopping(STOPREASONDISPLAY)) break;

		/* compute positions */
		slot = tr->position - sim_window_topline;
		if (slot < 0 || slot >= sim_window_vislines) continue;
		y_min = (sim_window_vislines - 1 - slot) * temp_trace_spacing + 31;

		/* draw the vertical line between the label and the trace */
		tx1 = 113;   ty1 = y_min;   sim_window_mapcoord(wavewin, &tx1, &ty1);
		tx2 = 113;   ty2 = y_min+temp_trace_spacing-10;   sim_window_mapcoord(wavewin, &tx2, &ty2);
		data[0] = 0;   data[1] = -(ty2-ty1)/2;
		data[2] = 0;   data[3] = (ty2-ty1)/2;
		ni = newnodeinst(art_openedpolygonprim, tx1, tx2, ty1, ty2, 0, 0, plotnp);
		if (ni == NONODEINST) { efree((char *)data);  return; }
		(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)data,
			VINTEGER|VISARRAY|(4<<VLENGTHSH));
		endobjectchange((INTBIG)ni, VNODEINST);

		/* write the text */
		(void)sprintf(s2, "%g", sim_window_anahigh);
		tx1 = 1;   ty1 = (y_min + y_max)/2;   sim_window_mapcoord(wavewin, &tx1, &ty1);
		ni = newnodeinst(gen_invispinprim, tx1, tx1, ty1, ty1, 0, 0, plotnp);
		if (ni == NONODEINST) { efree((char *)data);  return; }
		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)tr->name, VSTRING|VDISPLAY);
		if (var != NOVARIABLE)
		{
			TDSETSIZE(var->textdescript, TXTSETPOINTS(10));
			TDSETPOS(var->textdescript, VTPOSRIGHT);
		}
		endobjectchange((INTBIG)ni, VNODEINST);
		if ((tr->flags&TRACETYPE) == TRACEISANALOG)
		{
			tx1 = 0;     ty1 = y_min + tr->nameoff;
			sim_window_mapcoord(wavewin, &tx1, &ty1);
			tx2 = 100;   ty2 = y_min + tr->nameoff;
			sim_window_mapcoord(wavewin, &tx2, &ty2);
			data[0] = -(tx2-tx1)/2;  data[1] = 0;
			data[2] = (tx2-tx1)/2;   data[3] = 0;
			ni = newnodeinst(art_openedpolygonprim, tx1, tx2, ty1, ty2, 0, 0, plotnp);
			if (ni == NONODEINST) { efree((char *)data);  return; }
			(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)data,
				VINTEGER|VISARRAY|(4<<VLENGTHSH));
			(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, tr->color, VINTEGER);
			endobjectchange((INTBIG)ni, VNODEINST);
		}

		y_min = (sim_window_vislines - 1 - slot) * temp_trace_spacing + 31;
		y_max = y_min + temp_trace_spacing - 10;

		/* write busses */
		if ((tr->flags&TRACETYPE) == TRACEISBUS ||
			(tr->flags&TRACETYPE) == TRACEISOPENBUS)
		{
			/* find starting time and enumerate signals */
			sigcount = 0;
			for(bustr = sim_window_plot; bustr != NODISCHANNEL; bustr = bustr->nextdischannel)
			{
				if (bustr->buschannel != tr) continue;

				/* LINTED "time" used in proper order */
				if (sigcount == 0 || bustr->timearray[0] < time)
					time = bustr->timearray[0];
				bustr->busindex = sigcount++;
			}

			/* make sure there is room for all of the signal values */
			if (sigcount > sim_window_sigtotal)
			{
				newtotal = sim_window_sigtotal * 2;
				if (newtotal == 0) newtotal = 8;
				if (newtotal < sigcount) newtotal = sigcount;
				if (sim_window_sigtotal > 0) efree((char *)sim_window_sigvalues);
				sim_window_sigtotal = 0;
				sim_window_sigvalues = (INTBIG *)emalloc(newtotal * SIZEOFINTBIG, sim_tool->cluster);
				if (sim_window_sigvalues == 0) return;
				sim_window_sigtotal = newtotal;
			}

			/* draw the waveform */
			x_min = 114;
			for(;;)
			{
				/* compute signal value at time "time" */
				for(bustr = sim_window_plot; bustr != NODISCHANNEL; bustr = bustr->nextdischannel)
				{
					if (bustr->buschannel != tr) continue;
					for(j=0; j<bustr->numsteps; j++)
						if (time < bustr->timearray[j]) break;
					if (j > 0) j--;
					sim_window_sigvalues[bustr->busindex] = bustr->statearray[j] >> 8;
				}

				/* draw the value */
				x_max = sim_window_timetoxpos(time);
				if (x_max < 114) x_max = 114;
				if (x_max > 767) break;
				if (x_max > x_min)
				{
					tx1 = x_min;   ty1 = y_min;   sim_window_mapcoord(wavewin, &tx1, &ty1);
					tx2 = x_max;   ty2 = y_max;   sim_window_mapcoord(wavewin, &tx2, &ty2);
					cx = (tx1 + tx2) / 2;   cy = (ty1 + ty2) / 2;
					ni = newnodeinst(art_closedpolygonprim, tx1, tx2, ty1, ty2, 0, 0, plotnp);
					if (ni == NONODEINST) { efree((char *)data);  return; }
					busdescr[0] = x_min;          busdescr[1] = (y_min+y_max)/2;
					busdescr[2] = x_min+BUSANGLE; busdescr[3] = y_min;
					busdescr[4] = x_max-BUSANGLE; busdescr[5] = y_min;
					busdescr[6] = x_max;          busdescr[7] = (y_min+y_max)/2;
					busdescr[8] = x_max-BUSANGLE; busdescr[9] = y_max;
					busdescr[10] = x_min+BUSANGLE; busdescr[11] = y_max;
					for(j=0; j<12; j += 2)
					{
						sim_window_mapcoord(wavewin, &busdescr[j], &busdescr[j+1]);
						busdescr[j] -= cx;
						busdescr[j+1] -= cy;
					}
					(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)busdescr,
						VINTEGER|VISARRAY|(12<<VLENGTHSH));
					endobjectchange((INTBIG)ni, VNODEINST);

					/* draw the value string */
					strcpy(s2, sim_window_makewidevalue(sigcount, sim_window_sigvalues));
					tx1 = x_max+BUSANGLE/2;   ty1 = (y_min+y_max)/2;   sim_window_mapcoord(wavewin, &tx1, &ty1);
					ni = newnodeinst(gen_invispinprim, tx1, tx1, ty1, ty1, 0, 0, plotnp);
					if (ni == NONODEINST) { efree((char *)data);  return; }
					var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)s2, VSTRING|VDISPLAY);
					if (var != NOVARIABLE)
					{
						TDSETSIZE(var->textdescript, TXTSETPOINTS(10));
						TDSETPOS(var->textdescript, VTPOSRIGHT);
					}
					endobjectchange((INTBIG)ni, VNODEINST);
					x_min = x_max;
				}

				/* advance to the next time */
				nexttime = time;
				for(bustr = sim_window_plot; bustr != NODISCHANNEL; bustr = bustr->nextdischannel)
				{
					if (bustr->buschannel != tr) continue;
					for(j=0; j<bustr->numsteps; j++)
						if (bustr->timearray[j] > time) break;
					if (j < bustr->numsteps)
					{
						if (nexttime == time || bustr->timearray[j] < nexttime)
							nexttime = bustr->timearray[j];
					}
				}
				if (nexttime == time) break;
				time = nexttime;
			}
			if (x_max < 767)
			{
				x_min = x_max;   x_max = 767;
				tx1 = x_min;   ty1 = y_min;   sim_window_mapcoord(wavewin, &tx1, &ty1);
				tx2 = x_max;   ty2 = y_max;   sim_window_mapcoord(wavewin, &tx2, &ty2);
				cx = (tx1 + tx2) / 2;   cy = (ty1 + ty2) / 2;
				ni = newnodeinst(art_closedpolygonprim, tx1, tx2, ty1, ty2, 0, 0, plotnp);
				if (ni == NONODEINST) { efree((char *)data);  return; }
				busdescr[0] = x_min;          busdescr[1] = (y_min+y_max)/2;
				busdescr[2] = x_min+BUSANGLE; busdescr[3] = y_min;
				busdescr[4] = x_max-BUSANGLE; busdescr[5] = y_min;
				busdescr[6] = x_max;          busdescr[7] = (y_min+y_max)/2;
				busdescr[8] = x_max-BUSANGLE; busdescr[9] = y_max;
				busdescr[10] = x_min+BUSANGLE; busdescr[11] = y_max;
				for(j=0; j<12; j += 2)
				{
					sim_window_mapcoord(wavewin, &busdescr[j], &busdescr[j+1]);
					busdescr[j] -= cx;
					busdescr[j+1] -= cy;
				}
				(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)busdescr,
					VINTEGER|VISARRAY|(12<<VLENGTHSH));
				endobjectchange((INTBIG)ni, VNODEINST);
			}
			continue;
		}

		first = 1;
		datapos = 0;
		laststate = -1;
		for(j=0; j<tr->numsteps; j++)
		{
			time = tr->timearray[j];
			if ((tr->flags&TRACETYPE) == TRACEISDIGITAL)
			{
				/* digital waveform */
				if (j == tr->numsteps-1) x_max = 767; else
				{
					x_max = sim_window_timetoxpos(tr->timearray[j+1]);
					if (x_max < 114) continue;
				}
				x_min = sim_window_timetoxpos(time);
				if (x_min < 114) x_min = 114;
				if (x_min > 767) continue;

				strength = tr->statearray[j] & 0377;
				if (strength == 0) color = sim_colorstrengthoff; else
					if (strength <= NODE_STRENGTH) color = sim_colorstrengthnode; else
						if (strength <= GATE_STRENGTH) color = sim_colorstrengthgate; else
							color = sim_colorstrengthpower;

				/* LINTED "lastcolor" used in proper order */
				if (datapos > 0 && color != lastcolor)
				{
					/* strength has changed, dump out data so far */
					tx1 = 114;   ty1 = 31;    sim_window_mapcoord(wavewin, &tx1, &ty1);
					tx2 = 767;   ty2 = 570;   sim_window_mapcoord(wavewin, &tx2, &ty2);
					ni = newnodeinst(art_openedpolygonprim, tx1, tx2, ty1, ty2, 0, 0, plotnp);
					if (ni == NONODEINST) { efree((char *)data);  return; }
					(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)data,
						VINTEGER|VISARRAY|(datapos<<VLENGTHSH));
					(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, lastcolor, VINTEGER);
					endobjectchange((INTBIG)ni, VNODEINST);
					datapos = 0;
				}
				lastcolor = color;

				state = tr->statearray[j] >> 8;
				switch (state)
				{
					case LOGIC_LOW:
						if (laststate == LOGIC_HIGH)
							sim_window_addsegment(wavewin, data, &datapos, x_min, y_max, x_min, y_min);
						sim_window_addsegment(wavewin, data, &datapos, x_min, y_min, x_max, y_min);
						break;
					case LOGIC_HIGH:
						if (laststate == LOGIC_LOW)
							sim_window_addsegment(wavewin, data, &datapos, x_min, y_min, x_min, y_max);
						sim_window_addsegment(wavewin, data, &datapos, x_min, y_max, x_max, y_max);
						break;
					default:
						if (datapos > 0)
						{
							/* doing unknown block, dump out any line data */
							tx1 = 114;   ty1 = 31;    sim_window_mapcoord(wavewin, &tx1, &ty1);
							tx2 = 767;   ty2 = 570;   sim_window_mapcoord(wavewin, &tx2, &ty2);
							ni = newnodeinst(art_openedpolygonprim, tx1, tx2, ty1, ty2, 0, 0, plotnp);
							if (ni == NONODEINST) { efree((char *)data);  return; }
							(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)data,
								VINTEGER|VISARRAY|(datapos<<VLENGTHSH));
							(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, lastcolor,
								VINTEGER);
							endobjectchange((INTBIG)ni, VNODEINST);
						}
						datapos = 0;
						tx1 = x_min;   ty1 = y_min;   sim_window_mapcoord(wavewin, &tx1, &ty1);
						tx2 = x_max;   ty2 = y_max;   sim_window_mapcoord(wavewin, &tx2, &ty2);
						ni = newnodeinst(art_filledboxprim, tx1, tx2, ty1, ty2, 0, 0, plotnp);
						if (ni == NONODEINST) { efree((char *)data);  return; }
						(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, lastcolor, VINTEGER);
						endobjectchange((INTBIG)ni, VNODEINST);
						break;
				}
				laststate = state;
				first = 0;
			} else
			{
				/* analog waveform */
				tx = sim_window_timetoxpos(time);
				position = (double)(y_max - y_min);
				position *= (tr->valuearray[j] * sim_window_anarange - sim_window_anadislow) /
					sim_window_anadisrange;
				ty = (INTBIG)position + y_min;
				if (j != 0 && tx >= 114)
				{
					if (tx > 767)
					{
						/* "to" data point off the screen: interpolate */
						/* LINTED "fx" and "fy" used in proper order */
						ty = (ty-fy) * (767-fx) / (tx-fx) + fy;
						tx = 767;
					}

					if (first != 0 && fx < 114)
					{
						/* "from" data point off the screen: interpolate */
						fy = (ty-fy) * (114-fx) / (tx-fx) + fy;
						fx = 114;
					}
					first = 0;
					if (fy >= y_min && fy <= y_max &&
						ty >= y_min && ty <= y_max)
					{
						sim_window_addsegment(wavewin, data, &datapos, fx, fy, tx, ty);
					} else
					{
						cfx = fx;   cfy = fy;
						ctx = tx;   cty = ty;
						if (!clipline(&cfx, &cfy, &ctx, &cty, 114, 767, y_min, y_max))
						{
							sim_window_addsegment(wavewin, data, &datapos, cfx, cfy, ctx, cty);
						}
					}
				}
				fx = tx;   fy = ty;
				if (fx >= 767) break;
			}
		}
		if (datapos > 0)
		{
			tx1 = 114;   ty1 = 31;    sim_window_mapcoord(wavewin, &tx1, &ty1);
			tx2 = 767;   ty2 = 570;   sim_window_mapcoord(wavewin, &tx2, &ty2);
			ni = newnodeinst(art_openedpolygonprim, tx1, tx2, ty1, ty2, 0, 0, plotnp);
			if (ni == NONODEINST) { efree((char *)data);  return; }
			(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)data,
				VINTEGER|VISARRAY|(datapos<<VLENGTHSH));
			if ((tr->flags&TRACETYPE) == TRACEISANALOG)
				(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, tr->color, VINTEGER); else
					(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, lastcolor, VINTEGER);
			endobjectchange((INTBIG)ni, VNODEINST);
		}
	}

	/* ensure that the facet has the right size */
	(*el_curconstraint->solve)(plotnp);

	/* clean up */
	ttyputmsg("Created facet %s", describenodeproto(plotnp));
	efree((char *)data);
}

/*
 * Set the display color for strength "strength" to "color" (if "strength" is -1, show all colors).
 */
void sim_window_displaycolor(INTBIG strength, INTBIG color)
{
	char *colorname, *colorsymbol;

	if (strength == LOGIC_LOW) sim_colorlevellow = color; else
	if (strength == LOGIC_HIGH) sim_colorlevelhigh = color; else
	if (strength == LOGIC_X) sim_colorlevelundef = color; else
	if (strength == OFF_STRENGTH) sim_colorstrengthoff = color; else
	if (strength <= NODE_STRENGTH) sim_colorstrengthnode = color; else
	if (strength <= GATE_STRENGTH) sim_colorstrengthgate = color; else
	if (strength <= VDD_STRENGTH) sim_colorstrengthpower = color; else
	{
		if (ecolorname(sim_colorstrengthoff, &colorname, &colorsymbol))
			colorname = "**UNKNOWN**";
		ttyputmsg("Off-strength signals are colored %s", colorname);
		if (ecolorname(sim_colorstrengthnode, &colorname, &colorsymbol))
			colorname = "**UNKNOWN**";
		ttyputmsg("Node-strength signals are colored %s", colorname);
		if (ecolorname(sim_colorstrengthgate, &colorname, &colorsymbol))
			colorname = "**UNKNOWN**";
		ttyputmsg("Gate-strength signals are colored %s", colorname);
		if (ecolorname(sim_colorstrengthpower, &colorname, &colorsymbol))
			colorname = "**UNKNOWN**";
		ttyputmsg("Power-strength signals are colored %s", colorname);
		if (ecolorname(sim_colorlevellow, &colorname, &colorsymbol))
			colorname = "**UNKNOWN**";
		ttyputmsg("Low-level signals are colored %s", colorname);
		if (ecolorname(sim_colorlevelhigh, &colorname, &colorsymbol))
			colorname = "**UNKNOWN**";
		ttyputmsg("High-level signals are colored %s", colorname);
		if (ecolorname(sim_colorlevelundef, &colorname, &colorsymbol))
			colorname = "**UNKNOWN**";
		ttyputmsg("Undefined-level signals are colored %s", colorname);
	}
}

void sim_window_addsegment(WINDOWPART *wavewin, INTBIG *data, INTBIG *datapos, INTBIG fx, INTBIG fy,
	INTBIG tx, INTBIG ty)
{
	REGISTER INTBIG truefx, truefy, pos;
	INTBIG x, y;

	x = fx;   y = fy;   sim_window_mapcoord(wavewin, &x, &y);
	truefx = (x-sim_window_basex) - sim_window_offx;
	truefy = (y-sim_window_basey) - sim_window_offy;

	pos = *datapos;
	if (pos < 2 || data[pos-2] != truefx || data[pos-1] != truefy)
	{
		data[pos++] = truefx;
		data[pos++] = truefy;
	}

	x = tx;   y = ty;   sim_window_mapcoord(wavewin, &x, &y);
	data[pos++] = (x-sim_window_basex) - sim_window_offx;
	data[pos++] = (y-sim_window_basey) - sim_window_offy;
	*datapos = pos;
}

/*
 * Routine to remove trace "tr" from the waveform window's linked list.
 */
void sim_window_removetrace(DISCHANNEL *tr)
{
	DISCHANNEL *lasttr, *otr;

	lasttr = NODISCHANNEL;
	for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
	{
		if (otr == tr) break;
		lasttr = otr;
	}
	if (lasttr == NODISCHANNEL) sim_window_plot = tr->nextdischannel; else
		lasttr->nextdischannel = tr->nextdischannel;
}

/*
 * Routine to renumber the traces in the window.
 */
void sim_window_renumberlines(void)
{
	REGISTER INTBIG i, lastposition;
	REGISTER DISCHANNEL *tr, *otr;

#if 0
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
		tr->position = -1;
	for(i=0, tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
		if ((tr->flags&TRACENOTDRAWN) == 0) tr->position = i++;
#else
	i = 0;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) {tr->position = -1;   continue; }
		lastposition = tr->position;
		tr->position = i;
		for(otr = tr->nextdischannel; otr != NODISCHANNEL; otr = otr->nextdischannel)
		{
			if ((otr->flags&TRACENOTDRAWN) != 0) {otr->position = -1;   continue; }
			if (otr->position != lastposition) break;
			otr->position = i;
			tr = otr;
		}
		i++;
	}

#endif
	sim_window_lines = i;
}

/*
 * Routine to insert trace "tr" after "aftertr" in the waveform window's linked list.
 */
void sim_window_addtrace(DISCHANNEL *tr, DISCHANNEL *aftertr)
{
	if (aftertr == NODISCHANNEL)
	{
		tr->nextdischannel = sim_window_plot;
		sim_window_plot = tr;
	} else
	{
		tr->nextdischannel = aftertr->nextdischannel;
		aftertr->nextdischannel = tr;
	}
}

DISCHANNEL *sim_window_allocdischannel(void)
{
	REGISTER DISCHANNEL *tr;

	if (sim_window_plotfree == NODISCHANNEL)
	{
		tr = (DISCHANNEL *)emalloc(sizeof (DISCHANNEL), sim_tool->cluster);
		if (tr == 0) return(0);
	} else
	{
		tr = sim_window_plotfree;
		sim_window_plotfree = tr->nextdischannel;
	}
	tr->flags = 0;
	tr->timelimit = 0;
	tr->statelimit = 0;
	tr->valuelimit = 0;
	tr->numsteps = 0;
	tr->nameoff = 0;
	tr->buschannel = NODISCHANNEL;
	return(tr);
}

/********************************* LOW LEVEL GRAPHICS *********************************/

void sim_window_setviewport(WINDOWPART *w)
{
	float scalex, scaley;
	REGISTER INTBIG maxlines;

	w->screenlx = 0;   w->screenhx = 767;
	w->screenly = 0;   w->screenhy = 600;
	computewindowscale(w);

	scalex = (w->usehx - w->uselx) / 767.0f;
	scaley = ((w->usehy-20) - w->usely) / 600.0f;
	if (scalex > 2.0 && scaley > 2.0) sim_window_textsize = 0; else
		if (scalex > 1.0 && scaley > 1.0) sim_window_textsize = 16; else
			sim_window_textsize = 12;

	maxlines = ((w->usehy - w->usely) * 540 / 600) / 25;
	if (maxlines != sim_window_vislines)
	{
		if (sim_window_lines > sim_window_vislines)
			sim_window_vislines = sim_window_lines;
		if (sim_window_vislines > maxlines)
			sim_window_vislines = maxlines;
	}
}

/*
 * Routine to return the WINDOWPART that has the waveform display
 */
WINDOWPART *sim_window_findwaveform(void)
{
	WINDOWPART *w;

	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		if ((w->state & WINDOWTYPE) == WAVEFORMWINDOW)
			return(w);
	return(NOWINDOWPART);
}

/*
 * Routine to return the WINDOWPART that has the schematics/layout being simulated
 */
WINDOWPART *sim_window_findschematics(void)
{
	WINDOWPART *w;
	INTBIG state;

	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
	{
		state = w->state & (WINDOWTYPE|WINDOWSIMULATING);
		if (state == (DISPWINDOW|WINDOWSIMULATING) ||
			state == (DISP3DWINDOW|WINDOWSIMULATING))
				return(w);
	}
	return(NOWINDOWPART);
}

void sim_window_mapcoord(WINDOWPART *w, INTBIG *x, INTBIG *y)
{
	REGISTER INTBIG newx, newy;

	newx = w->uselx + applyxscale(w, *x - w->screenlx);
	newy = w->usely + (*y - w->screenly) *
		((w->usehy-20) - (w->usely)) / (w->screenhy - w->screenly);
	*x = newx;   *y = newy;
}

void sim_window_scaletowindow(WINDOWPART *w, INTBIG *x, INTBIG *y)
{
	*x = muldiv(*x - w->uselx, w->screenhx - w->screenlx,
		w->usehx - w->uselx) + w->screenlx;
	*y = muldiv(*y - w->usely, w->screenhy - w->screenly,
		(w->usehy-20) - w->usely) + w->screenly;
}

/* Some Control Sequences for the graphics terminal */
void sim_window_setcolor(INTBIG color)
{
	sim_window_desc.col = color;
}

void sim_window_setmask(INTBIG mask)
{
	sim_window_desc.bits = mask;
}

/* Graphics Commands */
void sim_window_moveto(INTBIG x, INTBIG y)
{
	sim_window_curx = x;
	sim_window_cury = y;
}

void sim_window_drawto(WINDOWPART *w, INTBIG x, INTBIG y)
{
	INTBIG fx, fy, tx, ty, cfx, cfy, ctx, cty;

	fx = sim_window_curx;   fy = sim_window_cury;
	tx = x;   ty = y;
	sim_window_mapcoord(w, &fx, &fy);
	sim_window_mapcoord(w, &tx, &ty);
	cfx = fx;   cfy = fy;
	ctx = tx;   cty = ty;
	if (!clipline(&cfx, &cfy, &ctx, &cty, w->uselx, w->usehx, w->usely, w->usehy))
	{
		fx = cfx;   fy = cfy;
		tx = ctx;   ty = cty;
		screendrawline(w, fx, fy, tx, ty, &sim_window_desc, 0);
	}
	sim_window_curx = x;
	sim_window_cury = y;
}

void sim_window_drawbox(WINDOWPART *w, INTBIG xll, INTBIG yll, INTBIG xur, INTBIG yur)
{
	INTBIG lx, ly, hx, hy, swap;

	lx = xll;   ly = yll;
	hx = xur;   hy = yur;
	sim_window_mapcoord(w, &lx, &ly);
	sim_window_mapcoord(w, &hx, &hy);
	if (lx > hx) { swap = lx;   lx = hx;   hx = swap; }
	if (ly > hy) { swap = ly;   ly = hy;   hy = swap; }
	screendrawbox(w, lx, hx, ly, hy, &sim_window_desc);
}

/*
 * Routine to draw string "s" in window "w" with highlighting and by erasing what
 * is underneath first
 */
void sim_window_drawhcstring(WINDOWPART *w, char *s)
{
	INTBIG xw, yw, oldcol, oldmask;
	INTBIG px, py;
	UINTBIG descript[TEXTDESCRIPTSIZE];

	px = sim_window_curx;   py = sim_window_cury;
	sim_window_mapcoord(w, &px, &py);

	TDCLEAR(descript);
	TDSETSIZE(descript, TXTSETPOINTS(sim_window_textsize));
	screensettextinfo(w, NOTECHNOLOGY, descript);
	screengettextsize(w, s, &xw, &yw);
	py -= yw/2;

	/* erase the area underneath */
	oldcol = sim_window_desc.col;
	oldmask = sim_window_desc.bits;
	sim_window_desc.col = ALLOFF;
	sim_window_desc.bits = LAYERA;
	screendrawbox(w, px, px+xw, py, py+yw, &sim_window_desc);
	sim_window_desc.col = oldcol;
	sim_window_desc.bits = oldmask;

	/* draw the text */
	screendrawtext(w, px, py, s, &sim_window_desc);
}

void sim_window_drawcstring(WINDOWPART *w, char *s)
{
	INTBIG wid, hei;
	INTBIG px, py;
	UINTBIG descript[TEXTDESCRIPTSIZE];

	px = sim_window_curx;   py = sim_window_cury;
	sim_window_mapcoord(w, &px, &py);
	TDCLEAR(descript);
	TDSETSIZE(descript, TXTSETPOINTS(sim_window_textsize));
	screensettextinfo(w, NOTECHNOLOGY, descript);
	screengettextsize(w, s, &wid, &hei);
	screendrawtext(w, px-wid/2, py, s, &sim_window_desc);
}

void sim_window_drawulstring(WINDOWPART *w, char *s)
{
	INTBIG wid, hei;
	INTBIG px, py;
	UINTBIG descript[TEXTDESCRIPTSIZE];

	px = sim_window_curx;   py = sim_window_cury;
	sim_window_mapcoord(w, &px, &py);
	TDCLEAR(descript);
	TDSETSIZE(descript, TXTSETPOINTS(sim_window_textsize));
	screensettextinfo(w, NOTECHNOLOGY, descript);
	screengettextsize(w, s, &wid, &hei);
	screendrawtext(w, px, py-hei, s, &sim_window_desc);
}

void sim_window_drawrstring(WINDOWPART *w, char *s)
{
	INTBIG x, y;
	INTBIG px, py;
	UINTBIG descript[TEXTDESCRIPTSIZE];

	TDCLEAR(descript);
	TDSETSIZE(descript, TXTSETPOINTS(sim_window_textsize));
	screensettextinfo(w, NOTECHNOLOGY, descript);
	screengettextsize(w, s, &x, &y);
	px = sim_window_curx;   py = sim_window_cury;
	sim_window_mapcoord(w, &px, &py);
	screendrawtext(w, px-x, py, s, &sim_window_desc);
}

/*
 * Name: sim_windowconvertengineeringnotation
 *
 * Description:
 *	This procedure converts a floating point number and represents time
 * in engineering units such as pico, micro, milli, etc.
 *
 * Calling Arguments:
 *	time = floating point value to be converted to Engineering notation
 *	s1   = pointer to a string that will be used to print the converted value
 */
void sim_windowconvertengineeringnotation(double time, char *s1)
{
	REGISTER INTBIG timeleft, timeright;
	INTBIG inttime;
	char *sectype;
	REGISTER char *negative;

	negative = "";
	if (time < 0.0)
	{
		negative = "-";
		time = - time;
	}
	if (doublesequal(time, 0.0))
	{
		(void)sprintf(s1, "0 %s", _("sec"));
		return;
	}
	if (time < 1.0E-15 || time >= 1000.0)
	{
		(void)sprintf(s1, "%s%g%s", negative, time, _("sec"));
		return;
	}

	sim_window_getproperunit(time, &sectype, &inttime);
	timeleft = inttime / 100;
	timeright = inttime % 100;
	if (timeright == 0)
	{
		(void)sprintf(s1, "%s%ld%s", negative, timeleft, sectype);
	} else
	{
		if ((timeright%10) == 0)
		{
			(void)sprintf(s1, "%s%ld.%ld%s", negative, timeleft, timeright/10, sectype);
		} else
		{
			(void)sprintf(s1, "%s%ld.%02ld%s", negative, timeleft, timeright, sectype);
		}
	}
}

/*
 * Routine to look at the "count" signal values in "value" and convert them to a readable
 * value in the current simulation display base.
 */
char *sim_window_makewidevalue(INTBIG count, INTBIG *value)
{
	REGISTER INTBIG i, j, k, val, base, groupsize;
	INTHUGE bigval;
	static BOOLEAN warned = FALSE;
	static char s2[MAXSIMWINDOWBUSWIDTH+3];
	static char *hexstring = "0123456789ABCDEF";

	/* if any values are undefined, the result is "XX" */
	for(i=0; i<count; i++)
		if (value[i] != LOGIC_LOW && value[i] != LOGIC_HIGH) return("XX");

	/* bases 2, 8 and 16 are simple */
	base = sim_window_state & BUSBASEBITS;
	if (base == BUSBASE2 || base == BUSBASE8 || base == BUSBASE16)
	{
		switch (base)
		{
			case BUSBASE2:  groupsize = 1;   break;
			case BUSBASE8:  groupsize = 3;   break;
			case BUSBASE16: groupsize = 4;   break;
		}
		j = MAXSIMWINDOWBUSWIDTH+3;
		s2[--j] = 0;
		for(i = count-1; i >= 0; i -= groupsize)
		{
			val = 0;
			for(k=0; k<groupsize; k++)
			{
				if (i-k < 0) break;
				if (value[i-k] == LOGIC_HIGH) val |= (1<<k);
			}
			s2[--j] = hexstring[val];
		}
		switch (base)
		{
			case BUSBASE2:  s2[--j] = 'x';  s2[--j] = '0';   break;
			case BUSBASE8:  s2[--j] = '0';                   break;
			case BUSBASE16: s2[--j] = 'b';  s2[--j] = '0';   break;
		}
		return(&s2[j]);
	}

	/* base 10 is harder: limit to 64 bits */
	if (count > 64 && !warned)
	{
		warned = TRUE;
		DiaMessageInDialog(_("Warning: cannot properly display busses greater than 64-bits wide in base 10"));
	}
	bigval = 0;
	for(i=0; i<count; i++)
	{
		bigval <<= 1;
		if (value[i] == LOGIC_HIGH) bigval |= 1;
	}
	return(hugeinttoa(bigval));
}

/* Simulation: Wide Value */
static DIALOGITEM sim_widedialogitems[] =
{
 /*  1 */ {0, {48,188,72,268}, BUTTON, N_("OK")},
 /*  2 */ {0, {12,188,36,268}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {8,8,24,168}, MESSAGE, N_("Value to set on bus:")},
 /*  4 */ {0, {32,8,48,168}, EDITTEXT, ""},
 /*  5 */ {0, {56,8,72,72}, MESSAGE, N_("Base:")},
 /*  6 */ {0, {56,76,72,144}, POPUP, ""}
};
static DIALOG sim_widedialog = {{75,75,156,352}, N_("Set Wide Value"), 0, 6, sim_widedialogitems};

/* special items for the "wide value" dialog: */
#define DSWV_VALUE     4		/* value (edit text) */
#define DSWV_BASE      6		/* base (popup) */

/*
 * Routine to prompt for a wide value and return the number of bits.
 * The bit values are stored in "bits".
 * Returns negative on abort/error.
 */
INTBIG sim_window_getwidevalue(INTBIG **bits)
{
	REGISTER INTBIG i, j, itemHit, groupsize, testbit, val;
	REGISTER UINTHUGE bigval, bigtestbit;
	REGISTER char *pt, chr;
	static INTBIG theBits[MAXSIMWINDOWBUSWIDTH];
	static INTBIG lastbase = 2;
	static char *base[4] = {"2", "8", "10", "16"};

	if (DiaInitDialog(&sim_widedialog)) return(-1);
	DiaSetPopup(DSWV_BASE, 4, base);
	DiaSetPopupEntry(DSWV_BASE, lastbase);
	DiaSetText(-DSWV_VALUE, "0");
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
	}
	lastbase = DiaGetPopupEntry(DSWV_BASE);
	if (lastbase == 2)
	{
		/* base 10 is special */
		bigval = 0;
		for(pt = DiaGetText(DSWV_VALUE); *pt != 0; pt++)
		{
			if (!isdigit(*pt)) break;
			bigval = bigval * 10 + (*pt - '0');
		}
		bigtestbit = INTHUGECONST(1) << 63;
		for(i=0; i<64; i++)
		{
			if ((bigval & bigtestbit) != 0) theBits[i] = 1; else
				theBits[i] = 0;
			bigtestbit >>= 1;
		}
		DiaDoneDialog();
		*bits = theBits;
		return(64);
	}

	/* bases 2, 8, and 16 are simpler */
	switch (lastbase)
	{
		case 0: groupsize = 1;   break;		/* base 2 */
		case 1: groupsize = 3;   break;		/* base 8 */
		case 3: groupsize = 4;   break;		/* base 16 */
	}
	j = 0;
	for(pt = DiaGetText(DSWV_VALUE); *pt != 0; pt++)
	{
		chr = tolower(*pt);
		if (chr >= '0' && chr <= '9') val = chr - '0'; else
			if (chr >= 'a' && chr <= 'f') val = chr - 'a' + 10; else
				val = 100;
		if (val >= (1 << groupsize))
		{
			DiaMessageInDialog(_("Invalid base %s value"), base[lastbase]);
			break;
		}
		testbit = 1 << (groupsize-1);
		for(i=0; i<groupsize; i++)
		{
			if ((val&testbit) != 0) theBits[j] = 1; else
				theBits[j] = 0;
			j++;
			testbit >>= 1;
		}
	}
	DiaDoneDialog();
	*bits = theBits;
	return(j);
}

/*
 * Routine to find the proper time unit to use when expressing "time".
 * Returns the type of seconds in "sectype" and returns an integer in
 * "inttime" that is 100 times the number of those seconds.
 */
void sim_window_getproperunit(double time, char **sectype, INTBIG *inttime)
{
	if (time * 1.0E17 < 100000.0)
	{
		*inttime = rounddouble(time * 1.0E17);
		*sectype = _("fsec");
		return;
	}
	if (time * 1.0E14 < 100000.0)
	{
		*inttime = rounddouble(time * 1.0E14);
		*sectype = _("psec");
		return;
	}
	if (time * 1.0E11 < 100000.0)
	{
		*inttime = rounddouble(time * 1.0E11);
		*sectype = _("nsec");
		return;
	}
	if (time * 1.0E8 < 100000.0)
	{
		*inttime = rounddouble(time * 1.0E8);
		*sectype = _("usec");
		return;
	}
	if (time * 1.0E5 < 100000.0)
	{
		*inttime = rounddouble(time * 1.0E5);
		*sectype = _("msec");
		return;
	}
	*inttime = rounddouble(time * 1.0E2);
	*sectype = _("sec");
}

#endif  /* SIMTOOL - at top */
