/* -*- tab-width: 4 -*-
 *
 * 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"
#include <math.h>
#include <float.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(f, np, chw, chs, fmt) Make simulation window with "f" frames, format "fmt"
 *                                              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
 * INTBIG sim_window_getnumframes()             Returns number of frames in sim window
 * sim_window_setnumframes(count)               Sets number of frames in simulation window
 * INTBIG sim_window_getnumvisframes()          Returns number of visible frames in sim window
 * INTBIG sim_window_gettopvisframe()           Returns topmost visible frame in simulation window
 * sim_window_settopvisframe(count)             Sets topmost visible frame in simulation window
 * sim_window_savegraph()                       Preserves plot in database
 * sim_window_writespicecmd()                   Writes vectors as SPICE commands
 * sim_window_displaycolor(s, c)                Set color of strength/state "s" to "c"
 *
 *     FRAME CONTROL
 * sim_window_auto_anarange(void)               Zooms all frames to fit their traces (TODO: all->frameno)
 * sim_window_zoom_frame(INTBIG frameno)        Zooms In frame "frameno" by scale 2
 * sim_window_zoomout_frame(INTBIG frameno)     Zooms Out frame "frameno" by scale 2
 * sim_window_shiftup_frame(INTBIG frameno)     Shifts Up frame "frameno" by 1/4 of height
 * sim_window_shiftdown_frame(INTBIG frameno)   Shifts Dowm frame "frameno" by 1/4 of height

 *     CONTROL OF TRACES:
 * INTBIG sim_window_newtrace(frame, name, data) Creates trace in "frame" 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_settraceframe(tr, frame)          Sets frame 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_gettraceframe(tr)          Returns frame number for trace "tr"
 * INTBIG sim_window_getcurframe()              Returns current frame number
 * 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
 * sim_window_clearframehighlight()             Unhighlights all frames (analog only)
 *
 *     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(tr, min, max)        Sets trace time range from "min" to "max"
 * sim_window_gettimerange(tr, min, max)        Returns trace 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 frame) */
	INTBIG    frameno;			/* frame 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) */
	double    mintime, maxtime;	/* displayed range along horozintal axis */
	float     anadislow;		/* low vertical axis for this trace (analog) */
	float     anadishigh;		/* high vertical axis for this trace (analog) */
	float     anadisrange;		/* vertical range for 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};

#define WAVEXNAMEEND     112
#define WAVEXNAMEENDANA  100
#define WAVEXBAR         113
#define WAVEXSIGSTART    114
#define WAVEXSIGEND      767
#define WAVEXSIGRANGE    (WAVEXSIGEND-WAVEXSIGSTART)

#define WAVEYTIMETOP      30
#define WAVEYSIGBOT       31
#define WAVEYSIGTOP      560
#define WAVEYSIGRANGE    (WAVEYSIGTOP-WAVEYSIGBOT)		/* was 540 */
#define WAVEYSIGGAP       10
#define WAVEYCURSTOP     571
#define WAVEYTEXTBOT     561
#define WAVEYTEXTTOP     600

#define MINTHUMB          25		/* minimum horizontal thumb size */

/* still need to convert "sim_window_drawcursors()" */

/*
 * 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 560 is signal waveforms
 *   31 to 571 is the cursors
 *  561 to 600 is text values (main and extension coordinates)
 */
static INTBIG        sim_window_curx, sim_window_cury;
static INTBIG        sim_window_textsize;
static INTBIG        sim_window_lines = 0;
static INTBIG        sim_window_lines_limit = 0;
static INTBIG        sim_window_visframes;		/* frames (or lines) visible in window */
static INTBIG        sim_window_topframes;		/* upper visible frame in window */
static INTBIG        sim_window_highframe;		/* highlighted frame in window */
static INTBIG        sim_window_txthei;
static INTBIG        sim_window_curanacolor;
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 float        sim_window_analow, sim_window_anahigh, sim_window_anarange;
static float        *sim_window_frame_analow=NULL, *sim_window_frame_anahigh=NULL;
static float        *sim_window_frame_topval=NULL, *sim_window_frame_botval=NULL;
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;
static INTBIG        sim_window_zoominix, sim_window_zoominiy;	/* initial zoom coordinate */
static INTBIG        sim_window_zoomothx, sim_window_zoomothy;	/* final zoom coordinate */
static INTBIG        sim_waveforminiththumbpos;
static double        sim_waveformhrange;
static float         sim_window_maxtime, sim_window_mintime;
static float	     sim_window_max_timetick, sim_window_min_timetick;
static float	     sim_window_time_separation;

/* 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_drawareazoom(WINDOWPART *w, BOOLEAN on, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty);
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_drawtimescale(WINDOWPART*);
static void        sim_window_drawto(WINDOWPART*, INTBIG, 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 BOOLEAN     sim_window_horizontalslider(INTBIG x, INTBIG y);
static void        sim_window_hthumbtrackingcallback(INTBIG delta);
static BOOLEAN     sim_window_lineseg(WINDOWPART *wavewin, INTBIG xf, INTBIG yf, INTBIG xt,
					INTBIG yt, INTBIG style, INTBIG x, INTBIG y);
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 BOOLEAN     sim_window_plottrace(DISCHANNEL *tr, WINDOWPART *wavewin, INTBIG style, INTBIG x, INTBIG y);
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_sethighlightframe(INTBIG frameno);
static void        sim_window_setmask(INTBIG);
static void        sim_window_setviewport(WINDOWPART*);
static void        sim_window_showtracebar(WINDOWPART *wavewin, INTBIG frame, INTBIG style);
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);
static BOOLEAN     sim_window_zoomdown(INTBIG, INTBIG);
static int         sim_window_set_frames(int n);
static float       sim_window_get_analow(int traceno);
static float       sim_window_get_anahigh(int traceno);
static float       sim_window_get_anarange(int traceno);
static void        sim_window_set_anahigh(int frameno, float value);
static void        sim_window_set_analow(int frameno, float value);
static void        sim_window_trace_range(INTBIG tri) ;
static float       sim_window_sensible_value(float *h, float *l, int *i1, int *i2, int n);
static void        sim_window_draw_timeticks(WINDOWPART *wavewin, INTBIG y1, INTBIG y2, BOOLEAN grid);
static float       sim_window_get_botval(int frameno);
static void        sim_window_set_topval(int frameno, float value);
static void        sim_window_set_botval(int frameno, float value);
static float       sim_window_get_extanarange(int frameno);
static void        sim_window_prettyprint(char *s, float v, int i1, int i2);

/********************************* 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_maincursor = 0.0000002f;
	sim_window_extensioncursor = 0.0000003f;
	(void)sim_window_set_frames(0) ;
	sim_window_visframes = 0;
	sim_window_topframes = 0;
	sim_window_highframe = -1;
	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;
	}
	if (sim_window_lines_limit > 0)
	{
		efree((char *)sim_window_frame_analow);
		efree((char *)sim_window_frame_anahigh);
		efree((char *)sim_window_frame_topval);
		efree((char *)sim_window_frame_botval);
	}
}

/*
 * routine to start a simulation window with room for "framecount" 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 framecount, 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 (framecount > 0)
	{
		sim_window_killalltraces(TRUE);
		if (sim_window_set_frames(framecount) == 0) return TRUE;
		sim_window_topframes = 0;
	}

	/* save simulation format */
	sim_window_format = format;

	/* determine the number of visible signals */
	sim_window_visframes = sim_window_lines;
	sim_window_curanacolor = 0;

	/* 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 frames in the simulation window to "count"
 */
void sim_window_setnumframes(INTBIG count)
{
	(void)sim_window_set_frames(count);
	if (sim_window_lines < sim_window_visframes)
		sim_window_visframes = sim_window_lines;
}

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

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

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

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

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

/*
 * routine to create a new trace to be displayed in window frame
 * "frameno".  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 frameno, 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;
	tr->mintime = sim_window_maincursor;
	tr->maxtime = sim_window_extensioncursor;

	/* 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 (frameno >= 0) tr->frameno = frameno; else
	{
		tr->frameno = -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();
	sim_window_clearframehighlight();

	/* 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;
		if (i == 0)
		{
			newtr->mintime = tr->mintime;
			newtr->maxtime = tr->maxtime;
		} else
		{
			if (tr->mintime < newtr->mintime) newtr->mintime = tr->mintime;
			if (tr->maxtime > newtr->maxtime) newtr->maxtime = tr->maxtime;
		}
	}

	/* 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;
	REGISTER void *infstr;

	/* 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)
	{
		infstr = initinfstr();
		tr = list[0];
		for(j=0; j<rootlen; j++) (void)addtoinfstr(infstr, tr->name[j]);

		for(i=0; i<count; i++)
		{
			tr = list[i];
			if (i != 0) addtoinfstr(infstr, ',');
			for(j=rootlen; tr->name[j] != ']' && tr->name[j] != 0; j++)
				addtoinfstr(infstr, 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)
			{
				formatinfstr(infstr, ":%ld", thisvalue);
				i = j;
			}
		}
		if (tr->name[rootlen-1] == '[') addtoinfstr(infstr, ']');
		return(returninfstr(infstr));
	}

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

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

	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 */
				infstr = initinfstr();
				addstringtoinfstr(infstr, tr->origname);
				for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
				{
					if (otr->buschannel != tr) continue;
					formatinfstr(infstr, "\t%ld:%s", otr->frameno, otr->origname);
				}
				(void)allocstring(&strings[j++], returninfstr(infstr), sim_tool->cluster);
			} else
			{
				infstr = initinfstr();
				formatinfstr(infstr, "%ld:%s", tr->frameno, tr->origname);
				(void)allocstring(&strings[j++], returninfstr(infstr), 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 = sim_window_anacolor[sim_window_curanacolor];
	sim_window_curanacolor++;
	if (sim_window_anacolor[sim_window_curanacolor] == 0) sim_window_curanacolor = 0;
	tr->numsteps = count;
	sim_window_trace_range(tri);
}

void sim_window_settraceframe(INTBIG tri, INTBIG frameno)
{
	REGISTER DISCHANNEL *tr;

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

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

	tr = (DISCHANNEL *)tri;
	if (tr == NODISCHANNEL || tr == 0) return;
	frameno = tr->frameno;
	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();
	sim_window_set_frames(1);
	sim_window_visframes=1;
	sim_window_topframes=0;
	sim_window_highframe=-1;
}

/********************************* 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 frame number associated with trace "tri"
 */
INTBIG sim_window_gettraceframe(INTBIG tri)
{
	REGISTER DISCHANNEL *tr;

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

/*
 * routine to return the current frame number
 */
INTBIG sim_window_getcurframe(void)
{
	return(sim_window_highframe);
}


/*
 * 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];
		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;
	REGISTER void *infstr;

	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 */
	infstr = initinfstr();
	addstringtoinfstr(infstr, name);
	addstringtoinfstr(infstr, "NV");
	tempname = returninfstr(infstr);
	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/FRAME HIGHLIGHTING *********************************/

/*
 * Routine to clear frame highlighting in the waveform window.
 */
void sim_window_clearframehighlight(void)
{
	REGISTER WINDOWPART *wavewin;

	if (sim_window_highframe >= 0)
	{
		wavewin = sim_window_findwaveform();
		if (wavewin == NOWINDOWPART) return;
		sim_window_setviewport(wavewin);
		sim_window_showtracebar(wavewin, sim_window_highframe, 2);
	}
	sim_window_highframe = -1;
}

/*
 * routine that adds trace "frameno" to the highlighted traces in the waveform window
 */
void sim_window_sethighlightframe(INTBIG frameno)
{
	REGISTER WINDOWPART *wavewin;

	if (frameno == sim_window_highframe) return;

	wavewin = sim_window_findwaveform();
	if (wavewin == NOWINDOWPART) return;
	sim_window_setviewport(wavewin);

	if (sim_window_highframe >= 0)
		sim_window_showtracebar(wavewin, sim_window_highframe, 2);
	sim_window_highframe = frameno;
	if (sim_window_highframe >= 0)
		sim_window_showtracebar(wavewin, sim_window_highframe, 1);
}

/*
 * 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++)
		(void)sim_window_plottrace(sim_window_highlightedtraces[i], wavewin, 2, 0, 0);
	sim_window_highlightedtracecount = 0;
	sim_window_drawcursors(wavewin);
}

/*
 * 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++)
		(void)sim_window_plottrace(sim_window_highlightedtraces[i], wavewin, 2, 0, 0);

	/* 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++)
		(void)sim_window_plottrace(sim_window_highlightedtraces[i], wavewin, 1, 0, 0);
	sim_window_drawcursors(wavewin);
}

/*
 * 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++)
			(void)sim_window_plottrace(sim_window_highlightedtraces[i], wavewin, 2, 0, 0);

		/* 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++)
			(void)sim_window_plottrace(sim_window_highlightedtraces[i], wavewin, 1, 0, 0);
	}
	sim_window_drawcursors(wavewin);
}

/*
 * 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 */
			(void)needstaticpolygon(&poly, 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 trace "tri" to run from time "min" to time "max"
 */
void sim_window_settimerange(INTBIG tri, double min, double max)
{
	REGISTER DISCHANNEL *tr, *otr;

	tr = (DISCHANNEL *)tri;
	for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
	{
		if (tr != 0 && otr->frameno != tr->frameno) continue;
		otr->mintime = min;
		otr->maxtime = max;
	}
}

/*
 * routine to return the range of time displayed in the simulation window
 */
void sim_window_gettimerange(INTBIG tri, double *min, double *max)
{
	REGISTER DISCHANNEL *tr;

	tr = (DISCHANNEL *)tri;
	*min = tr->mintime;
	*max = tr->maxtime;
}

/*
 * routine to return the average range of time displayed in the simulation window
 */
void sim_window_getaveragetimerange(double *totmintime, double *totmaxtime)
{
	double mintime, maxtime;
	REGISTER INTBIG tottraces, tr, j;

	sim_window_inittraceloop();
	*totmintime = *totmaxtime = 0.0;
	tottraces = 0;
	for(j=0; ; j++)
	{
		tr = sim_window_nexttraceloop();
		if (tr == 0) break;
		sim_window_gettimerange(tr, &mintime, &maxtime);
		*totmintime += mintime;
		*totmaxtime += maxtime;
		tottraces++;
	}
	if (tottraces > 0)
	{
		*totmintime /= tottraces;
		*totmaxtime /= tottraces;
	}
}

/*
 * 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;
		}
	}
	if (*min > 0.0) *min = 0.0;
}

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

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

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

	sim_window_drawareazoom(wavewin, FALSE, sim_window_zoominix, sim_window_zoominiy,
		sim_window_zoomothx, sim_window_zoomothy);
	sim_window_scaletowindow(wavewin, &x, &y);
	sim_window_zoomothx = x;
	sim_window_zoomothy = y;
	sim_window_drawareazoom(wavewin, TRUE, sim_window_zoominix, sim_window_zoominiy,
		sim_window_zoomothx, sim_window_zoomothy);
	return(FALSE);
}

void sim_window_drawareazoom(WINDOWPART *w, BOOLEAN on, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty)
{
	sim_window_setmask(LAYERH);
	if (on) sim_window_setcolor(HIGHLIT); else
		sim_window_setcolor(ALLOFF);
	sim_window_moveto(fx, fy);
	sim_window_drawto(w, fx, ty, 0);
	sim_window_drawto(w, tx, ty, 0);
	sim_window_drawto(w, tx, fy, 0);
	sim_window_drawto(w, fx, fy, 0);
}

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

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

	tr = (DISCHANNEL *)sim_window_plot;
	if (tr == NODISCHANNEL) return(FALSE);
	sim_window_scaletowindow(wavewin, &x, &y);
	sim_window_maincursor = sim_window_xpostotime(x);
	if (sim_window_maincursor < tr->mintime)
		sim_window_maincursor = tr->mintime;
	if (sim_window_maincursor > tr->maxtime)
		sim_window_maincursor = tr->maxtime;
	sim_window_drawcursors(wavewin);
	sim_window_updatelayoutwindow();
	return(FALSE);
}

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

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

	tr = (DISCHANNEL *)sim_window_plot;
	if (tr == NODISCHANNEL) return(FALSE);
	sim_window_scaletowindow(wavewin, &x, &y);
	sim_window_extensioncursor = sim_window_xpostotime(x);
	if (sim_window_extensioncursor < tr->mintime)
		sim_window_extensioncursor = tr->mintime;
	if (sim_window_extensioncursor > tr->maxtime)
		sim_window_extensioncursor = tr->maxtime;
	sim_window_drawcursors(wavewin);
	return(FALSE);
}

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

	if (delta == 0) return;
	sim_waveforminiththumbpos += delta;
	thumbarea = sim_waveformwindow->usehx - sim_waveformwindow->uselx - DISPLAYSLIDERSIZE*2;
	sim_window_gettimeextents(&mintime, &maxtime);
	range = maxtime * 2.0;
	range = sim_waveformhrange;
	sim_window_inittraceloop();
	for(j=0; ; j++)
	{
		tr = sim_window_nexttraceloop();
		if (tr == 0) break;
		sim_window_gettimerange(tr, &mintime, &maxtime);
		truerange = maxtime - mintime;

		mintime = (sim_waveforminiththumbpos -
			(sim_waveformwindow->uselx+DISPLAYSLIDERSIZE+2)) * range / thumbarea;
		if (mintime < 0.0) mintime = 0.0;
		maxtime = mintime + truerange;

		sim_window_settimerange(tr, mintime, maxtime);
	}
	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_visframes)
		point = sim_window_lines-sim_window_visframes;
	if (point == sim_window_topframes) return;
	sim_window_topframes = 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, frameno, lx, hx, ly, hy;

	/* compute positions */
	temp_trace_spacing = WAVEYSIGRANGE / sim_window_visframes;
	frameno = sim_window_tracedragtr->frameno - sim_window_topframes;
	if (frameno < 0 || frameno >= sim_window_visframes) return;
	y_pos = (sim_window_visframes - 1 - frameno) * temp_trace_spacing +
		WAVEYSIGBOT + sim_window_tracedragyoff;
	ly = y_pos + sim_window_tracedragtr->nameoff + 1;
	hy = ly + sim_window_txthei;
	lx = 0;
	hx = WAVEXNAMEEND;

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

/*
 * 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, 0);
}

/*
 * 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 = WAVEYSIGRANGE / sim_window_visframes;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		ind = tr->frameno - sim_window_topframes;
		ypos = (sim_window_visframes - 1 - ind) * temp_trace_spacing + WAVEYSIGBOT + 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 newtopframe;

	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_topframes + sim_window_visframes < sim_window_lines)
			{
				sim_window_topframes++;
				sim_window_redisphandler(sim_waveformwindow);
			}
			break;
		case 1: /* below thumb */
			if (y > sim_waveformwindow->thumbly) return(FALSE);
			newtopframe = sim_window_topframes + sim_window_visframes - 1;
			if (newtopframe + sim_window_visframes > sim_window_lines)
				newtopframe = sim_window_lines - sim_window_visframes;
			if (sim_window_topframes == newtopframe) break;
			sim_window_topframes = newtopframe;
			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_topframes == 0) return(FALSE);
			sim_window_topframes -= sim_window_visframes - 1;
			if (sim_window_topframes < 0) sim_window_topframes = 0;
			sim_window_redisphandler(sim_waveformwindow);
			break;
		case 4: /* up arrow clicked */
			if (y < sim_waveformwindow->usehy - DISPLAYSLIDERSIZE) return(FALSE);
			if (sim_window_topframes > 0)
			{
				sim_window_topframes--;
				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;
	REGISTER DISCHANNEL *tr;

	tr = (DISCHANNEL *)sim_window_plot;
	if (tr == NODISCHANNEL) return(FALSE);
	if (y >= sim_waveformwindow->usely) return(FALSE);
	switch (sim_waveformsliderpart)
	{
		case 0: /* left arrow clicked */
			if (x > sim_waveformwindow->uselx + DISPLAYSLIDERSIZE) return(FALSE);
			if (tr->mintime <= 0.0) break;
			range = tr->maxtime - tr->mintime;
			tr->mintime -= range/DISPLAYSMALLSHIFT;
			tr->maxtime -= range/DISPLAYSMALLSHIFT;
			if (tr->mintime < 0.0)
			{
				tr->maxtime -= tr->mintime;
				tr->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 (tr->mintime <= 0.0) break;
			range = tr->maxtime - tr->mintime;
			tr->mintime -= range/DISPLAYLARGESHIFT;
			tr->maxtime -= range/DISPLAYLARGESHIFT;
			if (tr->mintime < 0.0)
			{
				tr->maxtime -= tr->mintime;
				tr->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 = tr->maxtime - tr->mintime;
			tr->mintime += range/DISPLAYLARGESHIFT;
			tr->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 = tr->maxtime - tr->mintime;
			tr->mintime += range/DISPLAYSMALLSHIFT;
			tr->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, traceframesmoved, important;
	REGISTER INTBIG lx, hx, ly, hy, ind1, ind2, y_min, y_max;
	char *par[2], *butname;
	double range, highdisp, lowdisp, mintime, maxtime;
	float flow, fhigh;
	BOOLEAN areaselect;
	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;
			sim_window_gettimeextents(&mintime, &maxtime);
			sim_waveformhrange = maxtime * 2.0;
			sim_window_getaveragetimerange(&mintime, &maxtime);
			if (maxtime*2.0 > sim_waveformhrange) sim_waveformhrange = maxtime*2.0;

			sim_waveforminiththumbpos = w->thumblx;
			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 <= WAVEXNAMEEND)
	{
		temp_trace_spacing = WAVEYSIGRANGE / sim_window_visframes;
		ind = sim_window_visframes - 1 - (y - WAVEYSIGBOT) / temp_trace_spacing;
		for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
		{
			if ((tr->flags&TRACENOTDRAWN) == 0)
			{
				if (tr->frameno == ind + sim_window_topframes)
				{
					ypos = (sim_window_visframes - 1 - ind) * temp_trace_spacing + WAVEYSIGBOT + 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;
			sim_window_clearframehighlight();
			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->frameno = --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->frameno == tr->frameno) 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 */
			traceframesmoved = sim_window_tracedragyoff / temp_trace_spacing;
			if (traceframesmoved != 0)
			{
				/* erase highlighting */
				sim_window_cleartracehighlight();
				sim_window_clearframehighlight();

				/* 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->frameno == tr->frameno - traceframesmoved) 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->frameno < otr->frameno)
					{
						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_clearframehighlight();
			sim_window_addhighlighttrace((INTBIG)tr);
			if ((tr->flags&TRACETYPE) == TRACEISANALOG)
				sim_window_sethighlightframe(tr->frameno);
		} 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);
				if (tr->frameno == sim_window_highframe)
					sim_window_clearframehighlight();
			} else
			{
				/* signal new: add to highlight */
				sim_window_addhighlighttrace((INTBIG)tr);
			}

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

	/* see if an area-select was requested */
	butname = buttonname(button, &important);
	areaselect = FALSE;
#ifdef WIN32
	if (namesame(butname, "SARIGHT") == 0) areaselect = TRUE;
#endif
#ifdef MACOS
	if (namesame(butname, "SMOButton") == 0) areaselect = TRUE;
#endif
#ifdef ONUNIX
	if (namesame(butname, "SMRIGHT") == 0) areaselect = TRUE;
#endif
	if (areaselect)
	{
		/* select an area of the waveform window */
		sim_window_zoominix = sim_window_zoomothx = x;
		sim_window_zoominiy = sim_window_zoomothy = y;
		sim_window_drawareazoom(w, TRUE, sim_window_zoominix, sim_window_zoominiy,
			sim_window_zoomothx, sim_window_zoomothy);
		trackcursor(FALSE, us_nullup, us_nullvoid, sim_window_zoomdown,
			us_nullchar, us_nullvoid, TRACKDRAGGING);
		sim_window_drawareazoom(w, FALSE, sim_window_zoominix, sim_window_zoominiy,
			sim_window_zoomothx, sim_window_zoomothy);

		/* find the area */
		lx = mini(sim_window_zoominix, sim_window_zoomothx);
		hx = maxi(sim_window_zoominix, sim_window_zoomothx);
		ly = mini(sim_window_zoominiy, sim_window_zoomothy);
		hy = maxi(sim_window_zoominiy, sim_window_zoomothy);
		lowdisp = sim_window_xpostotime(lx);
		highdisp = sim_window_xpostotime(hx);
		range = highdisp - lowdisp;

		/* zoom into it horizontally */
		if (sim_window_maincursor < lowdisp || sim_window_maincursor > highdisp)
			sim_window_setmaincursor(lowdisp);
		if (sim_window_extensioncursor < lowdisp || sim_window_extensioncursor > highdisp)
			sim_window_setextensioncursor(highdisp);
/* #define ZOOMXSEPARATELY 1 */
#ifndef ZOOMXSEPARATELY
		sim_window_settimerange(0, lowdisp-range/15.0f, highdisp+range/15.0f);
#endif

		/* zoom vertically as well */
		temp_trace_spacing = WAVEYSIGRANGE / sim_window_visframes;
		ind1 = sim_window_visframes - 1 - (ly - WAVEYSIGBOT) / temp_trace_spacing;
		ind2 = sim_window_visframes - 1 - (hy - WAVEYSIGBOT) / temp_trace_spacing;
		y_min = (sim_window_visframes - 1 - ind1) * temp_trace_spacing + WAVEYSIGBOT;
		y_max = y_min + temp_trace_spacing - WAVEYSIGGAP;
		if (ind1 == ind2)
		{
			for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
			{

				if ((tr->flags&TRACENOTDRAWN) != 0) continue;
				if ((tr->flags & TRACETYPE) != TRACEISANALOG) continue;
				if (tr->frameno != ind1 + sim_window_topframes) continue;

				/* invert the display computation to get real units from screen coordinates */
				flow = ((ly-y_min) / (float)(y_max - y_min)) * sim_window_get_anarange(tr->frameno) + sim_window_get_analow(tr->frameno);
				fhigh = ((hy-y_min) / (float)(y_max - y_min)) * sim_window_get_anarange(tr->frameno) + sim_window_get_analow(tr->frameno);
				range = fhigh - flow;
				sim_window_set_analow(tr->frameno, flow-(float)range/15.0f);
				sim_window_set_anahigh(tr->frameno, fhigh+(float)range/15.0f);
#ifdef ZOOMXSEPARATELY 
				sim_window_settimerange((INTBIG)tr, lowdisp-range/15.0f, highdisp+range/15.0f);
#endif
			}
		} else
		{
#ifdef ZOOMXSEPARATELY 
			sim_window_settimerange(0, lowdisp-range/15.0f, highdisp+range/15.0f);
#endif
		}
		sim_window_redisphandler(w);
		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;
	}

	/* see if the line of a signal was hit */
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		if (sim_window_plottrace(tr, w, 3, x, y)) break;
	}
	if (tr != NODISCHANNEL)
	{
		/* highlight the selected signal */
		if (!shiftbutton(button))
		{
			/* shift not held: simply highlight this signal */
			sim_window_cleartracehighlight();
			sim_window_clearframehighlight();
			sim_window_addhighlighttrace((INTBIG)tr);
			if ((tr->flags&TRACETYPE) == TRACEISANALOG)
				sim_window_sethighlightframe(tr->frameno);
		} 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);
				if (tr->frameno == sim_window_highframe)
					sim_window_clearframehighlight();
			} else
			{
				/* signal new: add to highlight */
				sim_window_addhighlighttrace((INTBIG)tr);
			}

		}
		par[0] = "highlight";
		telltool(net_tool, 1, par);
	} else
	{
		sim_window_cleartracehighlight();
		sim_window_clearframehighlight();
	}
}

/*
 * 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;
	REGISTER DISCHANNEL *tr;
	REGISTER void *infstr;

	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_visframes * 100 / sim_window_lines;
		if (thumbsize >= 100) thumbsize = 100;
		if (thumbsize == 100 && sim_window_topframes == 0)
		{
			w->thumbly = 0;   w->thumbhy = -1;
		} else
		{
			thumbtop = sim_window_topframes * 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 */
	tr = (DISCHANNEL *)sim_window_plot;
	if (tr != NODISCHANNEL)
	{
		us_drawhorizontalslider(w, ly+DISPLAYSLIDERSIZE, lx+DISPLAYSLIDERSIZE, hx);
		thumbarea = w->usehx - w->uselx - DISPLAYSLIDERSIZE*2;
		if (tr->mintime <= 0.0)
		{
			w->thumblx = w->uselx+DISPLAYSLIDERSIZE*2+2;
			w->thumbhx = w->thumblx + thumbarea/2;
		} else
		{
			range = tr->maxtime * 2.0;
			w->thumblx = (INTBIG)(w->uselx+DISPLAYSLIDERSIZE*2+2 + tr->mintime/range*thumbarea);
			w->thumbhx = (INTBIG)(w->uselx+DISPLAYSLIDERSIZE*2+2 + tr->maxtime/range*thumbarea);
			if (w->thumbhx < w->thumblx + MINTHUMB) w->thumbhx = w->thumblx + MINTHUMB;
		}
		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;
	infstr = initinfstr();
	if ((w->state&WINDOWSIMULATING) == 0)
		addstringtoinfstr(infstr, _("Inactive Simulation Window, ? for help")); else
			addstringtoinfstr(infstr, _("Simulation Window, ? for help"));
	lx = w->uselx;
	screendrawtext(w, lx+5, hy-18, returninfstr(infstr), &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++)
		(void)sim_window_plottrace(sim_window_highlightedtraces[i], w, 1, 0, 0);
	if (sim_window_highframe >= 0)
		sim_window_showtracebar(w, sim_window_highframe, 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;
	REGISTER DISCHANNEL *tr;

	tr = sim_window_plot;
	if (tr == NODISCHANNEL) return(0);
	result = WAVEXSIGRANGE * (time - tr->mintime) / (tr->maxtime - tr->mintime) + WAVEXSIGSTART;
	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;
	REGISTER DISCHANNEL *tr;

	tr = sim_window_plot;
	if (tr == NODISCHANNEL) return(xpos);
	result = (xpos-WAVEXSIGSTART) * (tr->maxtime-tr->mintime) / WAVEXSIGRANGE + tr->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];
	float time;
	REGISTER DISCHANNEL *tr;
	int i1, i2;

	tr = sim_window_plot;
	if (tr == NODISCHANNEL) return;

	sim_window_setmask(LAYERA);
	sim_window_setcolor(MENTXT);
	sim_window_max_timetick = sim_window_maxtime = (float)tr->maxtime;
	sim_window_min_timetick = sim_window_mintime = (float)tr->mintime;
	sim_window_time_separation = sim_window_sensible_value(&sim_window_max_timetick, &sim_window_min_timetick, &i1, &i2, 5);
	if (sim_window_time_separation == 0.0) return;

	if (sim_window_max_timetick > sim_window_maxtime) sim_window_max_timetick -= sim_window_time_separation;
	time = sim_window_max_timetick;

	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 -= sim_window_time_separation;
	}
}

void sim_window_draw_timeticks(WINDOWPART *wavewin, INTBIG y1, INTBIG y2, BOOLEAN grid)
{
	INTBIG x_pos;
	float time;

	time = sim_window_max_timetick;

	for(;;)
	{
		if (time < sim_window_mintime) break;
		x_pos = sim_window_timetoxpos(time);

		if (grid == TRUE) {
			sim_window_moveto(x_pos, y1);
			sim_window_drawto(wavewin, x_pos, y2, 1);
		}

		sim_window_moveto(x_pos, y1);
		sim_window_drawto(wavewin, x_pos, y1+10, 0);
		sim_window_moveto(x_pos, y2);
		sim_window_drawto(wavewin, x_pos, y2-10, 0);
		time -= sim_window_time_separation;
	}
}

/*
 * 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);
}

/*
 * Routine to show the vertical bar between signal names and waveform data.
 * If "style" is 0, draw it solid
 * If "style" is 1, highlight it
 * If "style" is 2, unhighlight it
 */
void sim_window_showtracebar(WINDOWPART *wavewin, INTBIG frame, INTBIG style)
{
	INTBIG y_pos, temp_trace_spacing, visframe;

	temp_trace_spacing = WAVEYSIGRANGE / sim_window_visframes;
	visframe = frame - sim_window_topframes;
	if (visframe < 0 || visframe >= sim_window_visframes) return;
	y_pos = (sim_window_visframes - 1 - visframe) * temp_trace_spacing + WAVEYSIGBOT;

	/* draw the vertical line between the label and the trace */
	switch (style)
	{
		case 0:
			sim_window_setcolor(MENGLY);
			break;
		case 1:
			sim_window_setcolor(HIGHLIT);
			sim_window_setmask(LAYERH);
			break;
		case 2:
			sim_window_setcolor(ALLOFF);
			sim_window_setmask(LAYERH);
			break;
	}
	sim_window_moveto(WAVEXBAR, y_pos);
	sim_window_drawto(wavewin, WAVEXBAR, y_pos+temp_trace_spacing-WAVEYSIGGAP, 0);
	if (style != 0) sim_window_setmask(LAYERA);
}

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

	/* compute positions */
	temp_trace_spacing = WAVEYSIGRANGE / sim_window_visframes;
	frameno = tr->frameno - sim_window_topframes;
	if (frameno < 0 || frameno >= sim_window_visframes) return;
	y_pos = (sim_window_visframes - 1 - frameno) * temp_trace_spacing + WAVEYSIGBOT;

	/* draw the vertical line between the label and the trace */
	sim_window_showtracebar(wavewin, tr->frameno, 0);

	/* 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;
	if ((tr->flags&TRACETYPE) == TRACEISANALOG) textwid = WAVEXNAMEENDANA; else textwid = WAVEXNAMEEND;
	py = y_pos + tr->nameoff;
	maxheight = applyyscale(wavewin, temp_trace_spacing);
	maxwidth = applyxscale(wavewin, textwid-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;
	INTBIG lx, ly, hx, hy, swap;
	char s2[60], timeval[40];
	double mainv, extv, deltav;
	REGISTER DISCHANNEL *tr;

	/* erase all cursors */
	sim_window_setcolor(ALLOFF);
	sim_window_setmask(LAYERH);
	sim_window_drawbox(wavewin, WAVEXSIGSTART, 0, WAVEXSIGEND, WAVEYCURSTOP);
	sim_window_setcolor(HIGHLIT);

	/* draw main cursor */
	x_pos = sim_window_timetoxpos(sim_window_maincursor);
	if (x_pos >= WAVEXSIGSTART && x_pos <= WAVEXSIGEND)
	{
		sim_window_moveto(x_pos, WAVEYSIGBOT);
		sim_window_drawto(wavewin, x_pos, WAVEYCURSTOP, 0);
	}

	/* draw extension cursor */
	x_pos = sim_window_timetoxpos(sim_window_extensioncursor);
	if (x_pos >= WAVEXSIGSTART && x_pos <= WAVEXSIGEND)
	{
		sim_window_moveto(x_pos, WAVEYSIGBOT);
		sim_window_drawto(wavewin, x_pos, WAVEYCURSTOP, 0);
		sim_window_moveto(x_pos-5, WAVEYCURSTOP-12);
		sim_window_drawto(wavewin, x_pos+5, WAVEYCURSTOP-2, 0);
		sim_window_moveto(x_pos+5, WAVEYCURSTOP-12);
		sim_window_drawto(wavewin, x_pos-5, WAVEYCURSTOP-2, 0);
	}

	/* draw cursor locations textually */
	sim_window_setmask(LAYERA & ~LAYERH);
	sim_window_setcolor(ALLOFF);
	lx = WAVEXSIGSTART;   ly = WAVEYTEXTBOT;
	hx = WAVEXSIGEND;     hy = WAVEYTEXTTOP;
	sim_window_mapcoord(wavewin, &lx, &ly);
	sim_window_mapcoord(wavewin, &hx, &hy);
	if (lx > hx) { swap = lx;   lx = hx;   hx = swap; }
	if (ly > hy) { swap = ly;   ly = hy;   hy = swap; }
	screendrawbox(wavewin, lx, hx, ly+1, hy, &sim_window_desc);
	
	sim_window_setcolor(MENTXT);
	sim_window_moveto(WAVEXSIGSTART, WAVEYTEXTTOP);
	strcpy(s2, _("Main: "));
	sim_windowconvertengineeringnotation(sim_window_maincursor, timeval);
	strcat(s2, timeval);
	sim_window_drawulstring(wavewin, s2);

	if (sim_window_highlightedtracecount == 1)
	{
		tr = sim_window_highlightedtraces[0];
		if ((tr->flags&TRACETYPE) == TRACEISANALOG)
		{
			mainv = sim_window_getanatracevalue((INTBIG)tr, sim_window_maincursor);
			sim_window_moveto(WAVEXSIGSTART, 585);
			sprintf(s2, "Y=%g", mainv);
			sim_window_drawulstring(wavewin, s2);

			extv = sim_window_getanatracevalue((INTBIG)tr, sim_window_extensioncursor);
			sim_window_moveto(333, 585);
			sprintf(s2, "Y=%g", extv);
			sim_window_drawulstring(wavewin, s2);

			deltav = fabs(mainv-extv);
			sim_window_moveto(552, 585);
			sprintf(s2, "dY=%g", deltav);
			sim_window_drawulstring(wavewin, s2);
		}
	}

	sim_window_moveto(333, WAVEYTEXTTOP);
	strcpy(s2, _("Ext: "));
	sim_windowconvertengineeringnotation(sim_window_extensioncursor, timeval);
	strcat(s2, timeval);
	sim_window_drawulstring(wavewin, s2);

	sim_window_moveto(552, WAVEYTEXTTOP);
	strcpy(s2, _("Delta: "));
	deltav = sim_window_extensioncursor - sim_window_maincursor;
	if (deltav < 0.0) deltav = -deltav;
	sim_windowconvertengineeringnotation(deltav, timeval);
	strcat(s2, timeval);
	sim_window_drawulstring(wavewin, s2);
}

void sim_window_drawgraph(WINDOWPART *wavewin)
{
	INTBIG y_min, y_max, y_pos, maxoff, slot, i, temp_trace_spacing, range, frameno, lx, ly;
	BOOLEAN hasana, last, drawtext, grid=TRUE;
	UINTBIG descript[TEXTDESCRIPTSIZE];
	INTBIG x, y, px, py, wid, hei;
	float separation, topval, botval, val;
	char s2[15], *msg;
	REGISTER DISCHANNEL *tr, *otr;
	int vgrid;

	if (wavewin->state & GRIDON) grid = TRUE;
		else grid=FALSE;

	/* 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 = WAVEYSIGRANGE / sim_window_visframes;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		/* determine position of trace label on the line */
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		frameno = tr->frameno - sim_window_topframes;
		if (frameno < 0 || frameno >= sim_window_visframes) continue;
		if (tr->nameoff < 0)
		{
			hasana = FALSE;
			maxoff = 0;
			for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
			{
				if ((otr->flags&TRACENOTDRAWN) != 0) continue;
				if (otr->frameno != tr->frameno) continue;
				maxoff++;
				if ((otr->flags&TRACETYPE) == TRACEISANALOG) hasana = TRUE;
			}
			if (maxoff == 1)
			{
				/* only one trace: center it */
				tr->nameoff = (temp_trace_spacing - WAVEYSIGGAP - sim_window_txthei) / 2;
				if (tr->nameoff < 0) tr->nameoff = 0;
			} else
			{
				/* multiple traces: compute them */
				range = (temp_trace_spacing - WAVEYSIGGAP - 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->frameno != tr->frameno) continue;
					otr->nameoff = slot * range + sim_window_txthei;
					slot++;
				}
			}

			/* if there are analog traces on this line, show height values */
			if (hasana)
			{
				int i1, i2;

				y_min = (sim_window_visframes - 1 - (tr->frameno - sim_window_topframes)) *
					temp_trace_spacing + WAVEYSIGBOT;
				y_max = y_min + temp_trace_spacing - WAVEYSIGGAP;
				sim_window_setmask(LAYERA);
				sim_window_setcolor(MENTXT);
				topval = sim_window_get_anahigh(tr->frameno);
				botval = sim_window_get_analow(tr->frameno);
				vgrid = (y_max-y_min)/(2 * sim_window_txthei);
				if (vgrid < 1) vgrid = 1;
				if (vgrid > 4) vgrid = 4 + (vgrid -4)/3;
				separation = sim_window_sensible_value(&topval, &botval, &i1, &i2, vgrid);
				sim_window_set_topval(tr->frameno, topval);
				sim_window_set_botval (tr->frameno, botval);
				sim_window_moveto(WAVEXSIGSTART, y_min);
				sim_window_drawto(wavewin, WAVEXSIGSTART, y_max, 0);
				sim_window_drawto(wavewin, WAVEXSIGEND, y_max, 0);
				sim_window_drawto(wavewin, WAVEXSIGEND, y_min, 0);
				sim_window_drawto(wavewin, WAVEXSIGSTART, y_min, 0);
				sim_window_draw_timeticks(wavewin, y_min, y_max, grid);
				val = topval;
				for(i=0; ; i++)
				{
					if (val + 0.5f * separation < botval) break;

					y_pos = roundfloat((val-botval)/(topval-botval)*(float)(y_max-y_min))+y_min;
					sim_window_moveto(WAVEXNAMEENDANA, y_pos);
					sim_window_drawto(wavewin, WAVEXBAR, y_pos, 0);

					if (grid == TRUE) {
						sim_window_moveto(WAVEXSIGSTART, y_pos);
						sim_window_drawto(wavewin, WAVEXSIGEND, y_pos, 1);
					}

					if (val - 0.5f*separation < botval) last = TRUE; else
						last = FALSE;

					/* see if the text should be drawn */
					drawtext = FALSE;
					if (sim_window_txthei*2 < temp_trace_spacing)
					{
						if (sim_window_txthei*4 < temp_trace_spacing)
						{
							/*if ((i&1) == 0 || last)*/ drawtext = TRUE;
						} else
						{
							if (i == 0 || last) drawtext = TRUE;
						}
					}
					if (drawtext)
					{
						sim_window_prettyprint(s2, val, i1, i2);

						TDCLEAR(descript);
						TDSETSIZE(descript, TXTSETPOINTS(sim_window_textsize));
						screensettextinfo(wavewin, NOTECHNOLOGY, descript);
						screengettextsize(wavewin, s2, &x, &y);
						px = WAVEXNAMEENDANA;   py = y_pos;
						sim_window_mapcoord(wavewin, &px, &py);
						if (i == 0) py -= y; else
							if (!last) py -= y/2;
						screendrawtext(wavewin, px-x, py, s2, &sim_window_desc);
					}
					val -= separation;
				}
			}
		}
	}

	/* plot it all */
	if (sim_window_plot == NODISCHANNEL)
	{
		sim_window_setcolor(MENTXT);
		TDCLEAR(descript);
		TDSETSIZE(descript, TXTSETPOINTS(20));
		screensettextinfo(wavewin, NOTECHNOLOGY, descript);
		msg = _("Nothing plotted: type 'a' to add a signal");
		screengettextsize(wavewin, msg, &wid, &hei);
		lx = (wavewin->usehx + wavewin->uselx) / 2 - wid / 2;
		ly = (wavewin->usehy + wavewin->usely) / 2 - hei / 2;
		screendrawtext(wavewin, lx, ly, msg, &sim_window_desc);
	} else
	{
		for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
		{
			if ((tr->flags&TRACENOTDRAWN) != 0) continue;
			if (stopping(STOPREASONDISPLAY)) break;
			(void)sim_window_plottrace(tr, wavewin, 0, 0, 0);
		}
	}
}

/*
 * Routine to draw a trace "tr" in waveform window "wavewin".  "style" is:
 *  0   draw normal trace
 *  1   highlight trace
 *  2   unhighlight trace
 *  3   search for point (x,y) on trace, return TRUE if found
 */
BOOLEAN sim_window_plottrace(DISCHANNEL *tr, WINDOWPART *wavewin, INTBIG style, INTBIG x, INTBIG y)
{
	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 BOOLEAN hitpoint;
	REGISTER DISCHANNEL *bustr;
	double time, position, nexttime;

	/* get coordinates */
	if (sim_window_visframes == 0) return(FALSE);
	temp_trace_spacing = WAVEYSIGRANGE / sim_window_visframes;
	slot = tr->frameno - sim_window_topframes;
	if (slot < 0 || slot >= sim_window_visframes) return(FALSE);
	y_min = (sim_window_visframes - 1 - slot) * temp_trace_spacing + WAVEYSIGBOT;
	y_max = y_min + temp_trace_spacing - WAVEYSIGGAP;

	if (style == 1 || style == 2)
	{
		/* highlight/unhighlight trace name */
		ly = y_min + tr->nameoff+1;   hy = ly + sim_window_txthei;
		lx = 0;                       hx = WAVEXNAMEEND;
		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, 0);
		sim_window_drawto(wavewin, hx, hy, 0);
		sim_window_drawto(wavewin, lx, hy, 0);
		sim_window_drawto(wavewin, lx, ly, 0);
		sim_window_setmask(LAYERA);
		return(FALSE);
	}

	/* handle name field on the left */
	if (style == 0)
	{
		/* draw normal trace name */
		sim_window_writetracename(wavewin, tr);
	}
	hitpoint = FALSE;

	/* 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(FALSE);
			sim_window_sigtotal = newtotal;
		}

		/* draw the waveform */
		x_min = WAVEXSIGSTART;
		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 < WAVEXSIGSTART) x_max = WAVEXSIGSTART;
			if (x_max > WAVEXSIGEND) break;
			if (x_max > x_min)
			{
				if (style == 0)
				{
					sim_window_setcolor(sim_colorstrengthpower);
					sim_window_moveto(x_max+BUSANGLE/2, (y_min+y_max)/2);
					sim_window_drawhcstring(wavewin, s2);
				}
				hitpoint |= sim_window_lineseg(wavewin, x_min, (y_min+y_max)/2, x_min+BUSANGLE, y_min,
					style, x, y);
				hitpoint |= sim_window_lineseg(wavewin, x_min+BUSANGLE, y_min, x_max-BUSANGLE, y_min,
					style, x, y);
				hitpoint |= sim_window_lineseg(wavewin, x_max-BUSANGLE, y_min, x_max, (y_min+y_max)/2,
					style, x, y);
				hitpoint |= sim_window_lineseg(wavewin, x_max, (y_min+y_max)/2, x_max-BUSANGLE, y_max,
					style, x, y);
				hitpoint |= sim_window_lineseg(wavewin, x_max-BUSANGLE, y_max, x_min+BUSANGLE, y_max,
					style, x, y);
				hitpoint |= sim_window_lineseg(wavewin, x_min+BUSANGLE, y_max, x_min, (y_min+y_max)/2,
					style, x, y);
				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 < WAVEXSIGEND)
		{
			x_min = x_max;   x_max = WAVEXSIGEND;
			sim_window_setcolor(sim_colorstrengthpower);
			hitpoint |= sim_window_lineseg(wavewin, x_min, (y_min+y_max)/2, x_min+BUSANGLE, y_min,
				style, x, y);
			hitpoint |= sim_window_lineseg(wavewin, x_min+BUSANGLE, y_min, x_max-BUSANGLE, y_min,
				style, x, y);
			hitpoint |= sim_window_lineseg(wavewin, x_max-BUSANGLE, y_min, x_max, (y_min+y_max)/2,
				style, x, y);
			hitpoint |= sim_window_lineseg(wavewin, x_max, (y_min+y_max)/2, x_max-BUSANGLE, y_max,
				style, x, y);
			hitpoint |= sim_window_lineseg(wavewin, x_max-BUSANGLE, y_max, x_min+BUSANGLE, y_max,
				style, x, y);
			hitpoint |= sim_window_lineseg(wavewin, x_min+BUSANGLE, y_max, x_min, (y_min+y_max)/2,
				style, x, y);
		}
		return(hitpoint);
	}

	/* 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 = WAVEXSIGEND; else
			{
				x_max = sim_window_timetoxpos(tr->timearray[j+1]);
				if (x_max < WAVEXSIGSTART) continue;
				if (x_max > WAVEXSIGEND) x_max = WAVEXSIGEND;
			}
			x_min = sim_window_timetoxpos(time);
			if (x_min < WAVEXSIGSTART) x_min = WAVEXSIGSTART;
			if (x_min > WAVEXSIGEND) continue;

			state = tr->statearray[j] >> 8;
			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)
			{
				hitpoint |= sim_window_lineseg(wavewin, x_min, y_min, x_min, y_max,
					style, x, y);
			}
			first = 0;
			switch (state)
			{
				case LOGIC_LOW:
					hitpoint |= sim_window_lineseg(wavewin, x_min, y_min, x_max, y_min,
						style, x, y);
					break;
				case LOGIC_X:
					if (style == 3)
					{
						if (x >= x_min && x <= x_max && y >= y_min && y <= y_max)
							hitpoint = TRUE;
					} else
					{
						sim_window_drawbox(wavewin, x_min, y_min, x_max, y_max);
					}
					break;
				case LOGIC_HIGH:
					hitpoint |= sim_window_lineseg(wavewin, x_min, y_max, x_max, y_max,
						style, x, y);
					break;
				default:
					if (style == 3)
					{
						if (x >= x_min && x <= x_max && y >= y_min && y <= y_max)
							hitpoint = TRUE;
					} else
					{
						sim_window_drawbox(wavewin, x_min, y_min, x_max, y_max);
						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;
		}
		return(hitpoint);
	}

	/* 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_get_botval(tr->frameno)) / sim_window_get_extanarange(tr->frameno);
		ty = (INTBIG)position + y_min;
		if (j != 0 && tx >= WAVEXSIGSTART)
		{
			if (tx > WAVEXSIGEND)
			{
				/* "to" data point off the screen: interpolate */
				/* LINTED "fx" and "fy" used in proper order */
				ty = (ty-fy) * (WAVEXSIGEND-fx) / (tx-fx) + fy;
				tx = WAVEXSIGEND;
			}

			if (first != 0 && fx < WAVEXSIGSTART)
			{
				/* "from" data point off the screen: interpolate */
				fy = (ty-fy) * (WAVEXSIGSTART-fx) / (tx-fx) + fy;
				fx = WAVEXSIGSTART;
			}
			first = 0;
			sim_window_setcolor(tr->color);
			if (fy >= y_min && fy <= y_max &&
				ty >= y_min && ty <= y_max)
			{
				hitpoint |= sim_window_lineseg(wavewin, fx, fy, tx, ty, style, x, y);
			} else
			{
				cfx = fx;   cfy = fy;
				ctx = tx;   cty = ty;
				if (!clipline(&cfx, &cfy, &ctx, &cty, WAVEXSIGSTART, WAVEXSIGEND, y_min, y_max))
				{
					hitpoint |= sim_window_lineseg(wavewin, cfx, cfy, ctx, cty, style, x, y);
				}
			}
		}
		fx = tx;   fy = ty;
		if (fx >= WAVEXSIGEND) break;
	}
	return(hitpoint);
}

/*
 * Routine to draw from (xf,yf) to (xt,yt) in window "wavewin".  If "style" is zero, draw it.
 * If "style" is 3, do not draw, but return TRUE if (x,y) is on the line.
 */
BOOLEAN sim_window_lineseg(WINDOWPART *wavewin, INTBIG xf, INTBIG yf, INTBIG xt, INTBIG yt,
	INTBIG style, INTBIG x, INTBIG y)
{
	REGISTER INTBIG dist;

	if (style == 0)
	{
		sim_window_moveto(xf, yf);
		sim_window_drawto(wavewin, xt, yt, 0);
		return(FALSE);
	}
	dist = disttoline(xf, yf, xt, yt, x, y);
	if (dist < 5) return(TRUE);
	return(FALSE);
}

/*
 * 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, lastcolor, fx, fy, tx, ty, strength,
		sigcount, newtotal, cx, cy, *data, maxsteps;
	UINTBIG descript[TEXTDESCRIPTSIZE];
	REGISTER BOOLEAN hasana;
	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;
	REGISTER void *infstr;

	/* 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 */
	infstr = initinfstr();
	addstringtoinfstr(infstr, np->cell->cellname);
	addstringtoinfstr(infstr, "{sim}");
	plotnp = newnodeproto(returninfstr(infstr), 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 = WAVEXSIGSTART;   ty1 = WAVEYSIGBOT;    sim_window_mapcoord(wavewin, &tx1, &ty1);
	tx2 = WAVEXSIGEND;     ty2 = WAVEYCURSTOP;   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 = WAVEYTEXTTOP;   sim_window_mapcoord(wavewin, &tx1, &ty1);
	ni = newnodeinst(gen_invispinprim, tx1, tx1, ty1, ty1, 0, 0, plotnp);
	if (ni == NONODEINST) { efree((char *)data);  return; }
	infstr = initinfstr();
	addstringtoinfstr(infstr, "Simulation snapshot of facet ");
	addstringtoinfstr(infstr, describenodeproto(np));
	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)returninfstr(infstr), 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 = WAVEYSIGRANGE / sim_window_visframes;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) continue;
		slot = tr->frameno - sim_window_topframes;
		if (slot < 0 || slot >= sim_window_visframes) continue;

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

			/* if there are analog traces on this line, show height values */
			if (hasana)
			{
				y_min = (sim_window_visframes - 1 - (tr->frameno - sim_window_topframes)) *
					temp_trace_spacing + WAVEYSIGBOT;
				y_max = y_min + temp_trace_spacing - WAVEYSIGGAP;

				tx1 = WAVEXNAMEENDANA;   ty1 = y_min;   sim_window_mapcoord(wavewin, &tx1, &ty1);
				tx2 = WAVEXBAR;          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_get_analow(tr->frameno));
				tx1 = WAVEXNAMEEND;   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 = WAVEXNAMEENDANA;   ty1 = y_max;   sim_window_mapcoord(wavewin, &tx1, &ty1);
				tx2 = WAVEXBAR;          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_get_anahigh(tr->frameno));
				tx1 = WAVEXNAMEEND;   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);
			}
		}
	}

	/* plot time values */
	tr = sim_window_plot;
	if (tr != NODISCHANNEL)
	{
		separation = sim_window_sensibletimevalue((tr->maxtime - tr->mintime) / 5.0);
		time = sim_window_sensibletimevalue(tr->maxtime);
		while (time + separation <tr->maxtime)
			time += separation;
		for(;;)
		{
			if (time < tr->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->frameno - sim_window_topframes;
		if (slot < 0 || slot >= sim_window_visframes) continue;
		y_min = (sim_window_visframes - 1 - slot) * temp_trace_spacing + WAVEYSIGBOT;
		y_max = y_min + temp_trace_spacing - WAVEYSIGGAP;

		/* draw the vertical line between the label and the trace */
		tx1 = WAVEXBAR;   ty1 = y_min;   sim_window_mapcoord(wavewin, &tx1, &ty1);
		tx2 = WAVEXBAR;   ty2 = y_min+temp_trace_spacing-WAVEYSIGGAP;   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_get_anahigh(tr->frameno));
		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 = WAVEXNAMEENDANA;   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);
		}

		/* 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 = WAVEXSIGSTART;
			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 < WAVEXSIGSTART) x_max = WAVEXSIGSTART;
				if (x_max > WAVEXSIGEND) 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 < WAVEXSIGEND)
			{
				x_min = x_max;   x_max = WAVEXSIGEND;
				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 = WAVEXSIGEND; else
				{
					x_max = sim_window_timetoxpos(tr->timearray[j+1]);
					if (x_max < WAVEXSIGSTART) continue;
				}
				x_min = sim_window_timetoxpos(time);
				if (x_min < WAVEXSIGSTART) x_min = WAVEXSIGSTART;
				if (x_min > WAVEXSIGEND) 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 = WAVEXSIGSTART;   ty1 = WAVEYSIGBOT;    sim_window_mapcoord(wavewin, &tx1, &ty1);
					tx2 = WAVEXSIGEND;     ty2 = WAVEYCURSTOP;   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 = WAVEXSIGSTART;   ty1 = WAVEYSIGBOT;    sim_window_mapcoord(wavewin, &tx1, &ty1);
							tx2 = WAVEXSIGEND;     ty2 = WAVEYCURSTOP;   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_get_analow(tr->frameno)) / sim_window_get_anarange(tr->frameno);
				ty = (INTBIG)position + y_min;
				if (j != 0 && tx >= WAVEXSIGSTART)
				{
					if (tx > WAVEXSIGEND)
					{
						/* "to" data point off the screen: interpolate */
						/* LINTED "fx" and "fy" used in proper order */
						ty = (ty-fy) * (WAVEXSIGEND-fx) / (tx-fx) + fy;
						tx = WAVEXSIGEND;
					}

					if (first != 0 && fx < WAVEXSIGSTART)
					{
						/* "from" data point off the screen: interpolate */
						fy = (ty-fy) * (WAVEXSIGSTART-fx) / (tx-fx) + fy;
						fx = WAVEXSIGSTART;
					}
					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, WAVEXSIGSTART, WAVEXSIGEND, y_min, y_max))
						{
							sim_window_addsegment(wavewin, data, &datapos, cfx, cfy, ctx, cty);
						}
					}
				}
				fx = tx;   fy = ty;
				if (fx >= WAVEXSIGEND) break;
			}
		}
		if (datapos > 0)
		{
			tx1 = WAVEXSIGSTART;   ty1 = WAVEYSIGBOT;    sim_window_mapcoord(wavewin, &tx1, &ty1);
			tx2 = WAVEXSIGEND;     ty2 = WAVEYCURSTOP;   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;
}

#define SIMSTEPSIZE 1e-11f
#define EDGERATE    3e-11f
#define SUPPLY      1.8

/*
 * Routine to dump vectors in SPICE format.
 */
void sim_window_writespicecmd(void)
{
	FILE *fptr;
	int j, state, prevstate;
	double simDuration;
	char hvar[80], *pt;
	REGISTER DISCHANNEL *tr;
	REGISTER NETWORK *net;
	REGISTER PORTPROTO *pp;
	REGISTER NODEPROTO *facet, *simnt;
	REGISTER WINDOWPART *schemwin;

	/* get facet being saved */
	facet = getcurfacet();
	if (facet == NONODEPROTO) return;
	schemwin = sim_window_findschematics();
	if (schemwin == NOWINDOWPART)
	{
		ttyputerr(_("Must have original schematics or layout circuit as well as waveform"));
		return;
	}
	simnt = schemwin->curnodeproto;

	/* SPICE command file named fname.cmd.sp */
	fptr = xcreate(facet->cell->cellname, sim_filetypespicecmd, _("SPICE Command File"), &pt);
	if (fptr == NULL) return;
	ttyputmsg(_("Writing %s"), pt);

	/* Initialize variables */
	simDuration = 0.0;

	/* Create the SPICE command file */
	fprintf(fptr, "* %s\n", pt);
	fprintf(fptr, "* SPICE command file for facet %s from library %s\n",
		describenodeproto(facet), facet->cell->lib->libname);
	if ((us_useroptions&NODATEORVERSION) == 0)
	{
		(void)sprintf(hvar, "%s", timetostring(getcurrenttime()));
		fprintf(fptr, "* Written on %s by Electric VLSI Design System, %s\n\n",
			hvar, el_version);
	} else
	{
		fprintf(fptr, "* Written by Electric VLSI Design System\n\n");
	}

	fprintf(fptr, ".include %s.spi\n\n", facet->cell->cellname);

	fprintf(fptr, "* Power Supply\n");
	fprintf(fptr, ".param SUPPLY=%g\n", SUPPLY);
	fprintf(fptr, "VVDD VDD GND SUPPLY\n\n");

	fprintf(fptr, "* Inputs\n");
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		/* only interested in digital traces with information on them */
		if ((tr->flags&TRACETYPE) != TRACEISDIGITAL) continue;
		if (tr->numsteps <= 0) continue;

		/* find the export associated with this signal */
		net = getcomplexnetwork(tr->name, simnt);
		if (net == NONETWORK) continue;
		for(pp = simnt->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			if (pp->network == net) break;
		if (pp == NOPORTPROTO) continue;

		/* dump information for input and bidirectional exports */
		if ((pp->userbits&STATEBITS) == INPORT ||
			(pp->userbits&STATEBITS) == BIDIRPORT)
		{
			fprintf(fptr, "V%s %s GND PWL ", tr->name, tr->name);
			for(j=0; j<tr->numsteps; j++)
			{
				state = tr->statearray[j] >> 8;
				if (j == 0)
				{
					fprintf(fptr, "0 %s ", (state==LOGIC_HIGH) ? "SUPPLY" : "0");
				} else
				{
					pt = displayedunits((float)tr->timearray[j], VTUNITSTIME, INTTIMEUNITNSEC);
					fprintf(fptr, "%s %s ", pt, (prevstate==LOGIC_HIGH) ? "SUPPLY" : "0");
				}
				if (tr->timearray[j] > 0)
				{
					pt = displayedunits((float)tr->timearray[j]+EDGERATE, VTUNITSTIME, INTTIMEUNITNSEC);
					fprintf(fptr, "%s %s ", pt, (state==LOGIC_HIGH) ? "SUPPLY" : "0");
				}
				prevstate = state;
				if (tr->timearray[j] > simDuration) simDuration = tr->timearray[j];
			}
			fprintf(fptr, "\n");
		}
		fprintf(fptr, "R%s %s GND 1G\n", tr->name, tr->name);
	}
	fprintf(fptr, "\n");

	fprintf(fptr, "* Simulation Commands\n");
	fprintf(fptr, ".options post\n");
	strcpy(hvar, displayedunits(SIMSTEPSIZE, VTUNITSTIME, INTTIMEUNITNSEC));
	pt = displayedunits((float)simDuration+30.0f*EDGERATE, VTUNITSTIME, INTTIMEUNITNSEC);
	fprintf(fptr, ".tran %s %s\n", hvar, pt);
	fprintf(fptr, ".end\n");
	xclose(fptr);
}

/*
 * 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, lastframeno;
	REGISTER DISCHANNEL *tr, *otr;

	i = 0;
	for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
	{
		if ((tr->flags&TRACENOTDRAWN) != 0) {tr->frameno = -1;   continue; }
		lastframeno = tr->frameno;
		tr->frameno = i;
		for(otr = tr->nextdischannel; otr != NODISCHANNEL; otr = otr->nextdischannel)
		{
			if ((otr->flags&TRACENOTDRAWN) != 0) {otr->frameno = -1;   continue; }
			if (otr->frameno != lastframeno) break;
			otr->frameno = i;
			tr = otr;
		}
		i++;
	}

	(void)sim_window_set_frames(i);
	sim_window_auto_anarange();
}

/*
 * 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 = WAVEXSIGEND;
	w->screenly = 0;   w->screenhy = WAVEYTEXTTOP;
	computewindowscale(w);

	scalex = (w->usehx - w->uselx) / (float)WAVEXSIGEND;
	scaley = ((w->usehy-20) - w->usely) / (float)WAVEYTEXTTOP;
	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) * WAVEYSIGRANGE / WAVEYTEXTTOP) / 25;
	if (maxlines != sim_window_visframes)
	{
		if (sim_window_lines > sim_window_visframes)
			sim_window_visframes = sim_window_lines;
		if (sim_window_visframes > maxlines)
			sim_window_visframes = 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 texture)
{
	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, texture);
	}
	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);
}

/*
 * 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;
	}

	/* get proper time unit to use */
	if (time * 1.0E17 < 100000.0)
	{
		inttime = rounddouble(time * 1.0E17);
		sectype = _("fsec");
	} else if (time * 1.0E14 < 100000.0)
	{
		inttime = rounddouble(time * 1.0E14);
		sectype = _("psec");
	} else if (time * 1.0E11 < 100000.0)
	{
		inttime = rounddouble(time * 1.0E11);
		sectype = _("nsec");
	} else if (time * 1.0E8 < 100000.0)
	{
		inttime = rounddouble(time * 1.0E8);
		sectype = _("usec");
	} else if (time * 1.0E5 < 100000.0)
	{
		inttime = rounddouble(time * 1.0E5);
		sectype = _("msec");
	} else
	{
		inttime = rounddouble(time * 1.0E2);
		sectype = _("sec");
	}
	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, 0, 0};

/* 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;
	REGISTER void *dia;
	static INTBIG theBits[MAXSIMWINDOWBUSWIDTH];
	static INTBIG lastbase = 2;
	static char *base[4] = {"2", "8", "10", "16"};

	dia = DiaInitDialog(&sim_widedialog);
	if (dia == 0) return(-1);
	DiaSetPopup(dia, DSWV_BASE, 4, base);
	DiaSetPopupEntry(dia, DSWV_BASE, lastbase);
	DiaSetText(dia, -DSWV_VALUE, "0");
	for(;;)
	{
		itemHit = DiaNextHit(dia);
		if (itemHit == OK || itemHit == CANCEL) break;
	}
	lastbase = DiaGetPopupEntry(dia, DSWV_BASE);
	if (lastbase == 2)
	{
		/* base 10 is special */
		bigval = 0;
		for(pt = DiaGetText(dia, 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(dia);
		*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(dia, 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(dia);
	*bits = theBits;
	return(j);
}

void sim_window_auto_anarange(void)
{
	DISCHANNEL *tr;
	int i, *t;
	double l, h;

	if (sim_window_lines > 0)
		t = (int *)emalloc(sim_window_lines*sizeof(int), sim_tool->cluster);
	else
		t = NULL;
	for(i=0; i < sim_window_lines; i++) t[i] = 0;

	for(i=0, tr = sim_window_plot; tr != NODISCHANNEL; ++i, tr = tr->nextdischannel) {

		if (i == 0 ) {
			l  = tr->anadislow;
			h = tr->anadishigh;
		} else {
			if ( tr->anadislow  < l ) l = tr->anadislow;
			if ( tr->anadishigh > h ) h = tr->anadishigh;
		}

		if (tr->frameno < sim_window_lines && tr->frameno >=0 ) {
			if (t[tr->frameno] == 0) {
				sim_window_frame_analow[tr->frameno] = tr->anadislow;
				sim_window_frame_anahigh[tr->frameno] = tr->anadishigh;
				t[tr->frameno] = 1;
			} else {
				if (sim_window_frame_analow[tr->frameno] > tr->anadislow)
					sim_window_frame_analow[tr->frameno] = tr->anadislow;

				if (sim_window_frame_anahigh[tr->frameno] < tr->anadishigh)
					sim_window_frame_anahigh[tr->frameno] = tr->anadishigh;
			}
		}
	}

	if (t) efree((char *)t);

	sim_window_analow = (float)l;
	sim_window_anahigh = (float)h;
	sim_window_anarange = (float)(h - l);

}

void sim_window_trace_range(INTBIG tri)
{
	DISCHANNEL *tr;
	int j;
	float h, l;

	tr = (DISCHANNEL *)tri;

	if (tr->numsteps >0) {
		h = tr->valuearray[0];
		l = tr->valuearray[0];
	}

	for (j=1; j<tr->numsteps; ++j) {
		if ( tr->valuearray[j] < l ) l = tr->valuearray[j];
		if ( tr->valuearray[j] > h ) h = tr->valuearray[j];
	}

/*	d = fabs(h-l);

	if (d/(h+l)<DBL_EPSILON) d = 0.5;
	m=1.0;
	while ( d > 10.0 ) { d/=10.0; m*=10.0; }
	while ( d < 1.0  ) { d*=10.0; m/=10.0; }

	li = (int)(l/m);
	hi = (int)(h/m);
	if (li<0) --li;
	if (hi>0) ++hi;
	l = (float)((double)li*m);
	h = (float)((double)hi*m);
*/
	tr->anadislow   = l;
	tr->anadishigh  = h;
	tr->anadisrange = h-l;
}

int sim_window_set_frames(int n)
{

	int i;
	INTBIG newlimit;
	float *newanalow, *newanahigh, *newtopval, *newbotval;

	if (n > sim_window_lines_limit)
	{
		newlimit = (sim_window_lines_limit < 16 ? 16 : sim_window_lines_limit);
		while(newlimit < n) newlimit *= 2;
		newanalow = (float *)emalloc(newlimit*sizeof(float), sim_tool->cluster);
		newanahigh = (float *)emalloc(newlimit*sizeof(float), sim_tool->cluster);
		newtopval = (float *)emalloc(newlimit*sizeof(float), sim_tool->cluster);
		newbotval = (float *)emalloc(newlimit*sizeof(float), sim_tool->cluster);
		if(newanalow == NULL || newanahigh == NULL || newtopval == NULL || newbotval == NULL)
		{
			if(newanalow != NULL) efree((char *)newanalow);
			if(newanahigh != NULL) efree((char *)newanahigh);
			if(newtopval != NULL) efree((char *)newtopval);
			if(newbotval != NULL) efree((char *)newbotval);
			return 0;
		}
		for (i=0; i<sim_window_lines; ++i) {
			newanalow[i] = sim_window_frame_analow[i];
			newanahigh[i] = sim_window_frame_anahigh[i];
			newtopval[i] = sim_window_frame_topval[i];
			newbotval[i] = sim_window_frame_botval[i];
		}
		if (sim_window_lines_limit > 0)
		{
			efree((char *)sim_window_frame_analow);
			efree((char *)sim_window_frame_anahigh);
			efree((char *)sim_window_frame_topval);
			efree((char *)sim_window_frame_botval);
		}
		sim_window_frame_analow = newanalow;
		sim_window_frame_anahigh = newanahigh;
		sim_window_frame_topval = newtopval;
		sim_window_frame_botval = newbotval;
		sim_window_lines_limit = newlimit;
	}

	for (i=sim_window_lines; i<n; ++i) {
		sim_window_frame_analow[i]  = sim_window_analow;
		sim_window_frame_anahigh[i] = sim_window_anahigh;
		sim_window_frame_topval[i] = sim_window_analow;
		sim_window_frame_botval[i] = sim_window_anahigh;
	}

	sim_window_lines = n;
	return 1;
}

float sim_window_get_analow(int frameno)
{
	if (frameno >=0 && frameno < sim_window_lines)
		return sim_window_frame_analow[frameno];
	else
		return sim_window_analow;
}

float sim_window_get_anahigh(int frameno)
{
	if (frameno >=0 && frameno < sim_window_lines)
		return sim_window_frame_anahigh[frameno];
	else
		return sim_window_anahigh;
}

#if 0		/* never called */
float sim_window_get_topval(int frameno)
{
	if (frameno >=0 && frameno < sim_window_lines)
		return sim_window_frame_topval[frameno];
	else
		return sim_window_anahigh;
}
#endif

float sim_window_get_botval(int frameno)
{
	if (frameno >=0 && frameno < sim_window_lines)
		return sim_window_frame_botval[frameno];
	else
		return sim_window_analow;
}

void sim_window_set_anahigh(int frameno, float value)
{
	if (frameno >=0 && frameno < sim_window_lines)
		sim_window_frame_anahigh[frameno] = value;
}

void sim_window_set_analow(int frameno, float value)
{
	if (frameno >=0 && frameno < sim_window_lines)
		sim_window_frame_analow[frameno] = value;
}

void sim_window_set_topval(int frameno, float value)
{
	if (frameno >=0 && frameno < sim_window_lines)
		sim_window_frame_topval[frameno] = value;
}

void sim_window_set_botval(int frameno, float value)
{
	if (frameno >=0 && frameno < sim_window_lines)
		sim_window_frame_botval[frameno] = value;
}

float sim_window_get_anarange(int frameno)
{
	if (frameno >=0 && frameno < sim_window_lines)
		return sim_window_frame_anahigh[frameno] - sim_window_frame_analow[frameno];
	else
		return sim_window_anarange;
}

float sim_window_get_extanarange(int frameno)
{
	if (frameno >=0 && frameno < sim_window_lines)
		return sim_window_frame_topval[frameno] - sim_window_frame_botval[frameno];
	else
		return sim_window_anarange;
}

void sim_window_zoom_frame(INTBIG frameno)
{
	float v;
	if (frameno >=0 && frameno < sim_window_lines) {
		v = sim_window_get_anarange(frameno);
		v /= 4.0f;
		sim_window_frame_anahigh[frameno] -=v;
		sim_window_frame_analow[frameno] +=v;
	}
}

void sim_window_zoomout_frame(INTBIG frameno)
{
	float v;
	if (frameno >=0 && frameno < sim_window_lines) {
		v = sim_window_get_anarange(frameno);
		v /= 2.0f;
		sim_window_frame_anahigh[frameno] +=v;
		sim_window_frame_analow[frameno] -=v;
	}
}

void sim_window_shiftup_frame(INTBIG frameno)
{
	float v;
	if (frameno >=0 && frameno < sim_window_lines) {
		v = sim_window_get_anarange(frameno);
		v /= 4.0f;
		sim_window_frame_anahigh[frameno] +=v;
		sim_window_frame_analow[frameno] +=v;
	}
}

void sim_window_shiftdown_frame(INTBIG frameno)
{
	float v;
	if (frameno >=0 && frameno < sim_window_lines) {
		v = sim_window_get_anarange(frameno);
		v /= 4.0f;
		sim_window_frame_anahigh[frameno] -=v;
		sim_window_frame_analow[frameno] -=v;
	}
}

/* This function takes a high value h, a low value l, chops the interval between
h and l in n bits, and rounds the high, low and the interval to a 1,2,5
range.  The integers i1 and i2 are the powers of 10 that belong to the
largest value in the interval and the step size.  These integers can be
used for printing the scale. */

float sim_window_sensible_value(float *h, float *l, int *i1, int *i2, int n)
{
	float d, m, max;
	int di, hi, li;

	if (fabs(*h) > fabs(*l)) max = (float)fabs(*h); else max = (float)fabs(*l);
	*i1 = *i2 = 0;
	if (max == 0.0) return(0.0);

	while ( max >= 10.0 ) { max/=10.0; ++*i1; }
	while ( max <= 1.0  ) { max*=10.0; --*i1; }

	d = (float)fabs(*h - *l)/(float)n;

	if ((float)fabs(d/(*h+*l))<FLT_EPSILON) d = 0.1f;
	m=1.0;
	while ( d >= 10.0 ) { d/=10.0; m*=10.0; ++*i2; }
	while ( d <= 1.0  ) { d*=10.0; m/=10.0; --*i2; }

	di = (int)d;

	if (di > 2 && di <= 5) di=5;
	else 
		if (di > 5) di = 10;

	li = (int)(*l/m);
	hi = (int)(*h/m);
	li = (li/di)*di;
	hi = (hi/di)*di;
	if (li<0) li-=di;
	if (hi>0) hi+=di;
	*l = (float)((double)li*m);
	*h = (float)((double)hi*m);

	return (float)di*m;
}

void sim_window_prettyprint(char *s, float v, int i1, int i2)
{

	double d;
	int i, p;

	d = 1.0;
	if (i2 > 0)
		for (i = 0; i < i2; ++i) d *= 10.0;
	if (i2 < 0)
		for (i = 0; i > i2; --i) d /= 10.0;

	if (fabs((double)v)*100.0 < d) {
		sprintf(s, "0");
	} else {

		if (i1 <= 4 && i1 >=0 && i2 >=0) {
			sprintf(s, "%.0f", v);
		} else {
			if (i1 <= 4 && i1 >= -2 && i2 < 0) {
				sprintf(s, "%.*f", (-i2), v);
			} else {
				p = i1-12-1;

				if (p < 0) p=0;

				sprintf(s, "%.*fe%+2d", p, (double)v/d, i2);
			}
		}
	}
}

#endif  /* SIMTOOL - at top */
