/*
 * Electric(tm) VLSI Design System
 *
 * File: simwindow.c
 * Simulation aid: 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 SIMAID

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

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

#define	NODISCHANNEL ((DISCHANNEL *)-1)

typedef struct Idischannel
{
	char     *name;			/* name of this trace */
	INTBIG    nodeptr;		/* "user" data for this trace */
	INTSML    status;		/* state of this trace (0 for digital, color for analog) */
	INTSML    nameoff;		/* offset of trace name (when many per line) */
	INTSML    position;		/* line number of this trace in window */
	INTSML    numsteps;		/* number of steps along this trace */
	INTSML    timelimit;	/* maximum times allocated for this trace */
	INTSML    statelimit;	/* maximum states allocated for this trace */
	INTSML    valuelimit;	/* maximum values allocated for this trace */
	float    *timearray;	/* time steps along this trace */
	INTSML   *statearray;	/* states along this trace (digital) */
	float    *valuearray;	/* values along this trace (analog) */
	struct Idischannel *nextdischannel;
} DISCHANNEL;

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

/*
 * In X:
 *    0 to 112 is signal name
 *      113    is bar
 *   114-767 is signal waveform
 * In Y:
 *    0 to  30 is axis label
 *   31 to 570 is signal waveforms
 *  571 to 600 is cursor value
 */
static INTSML        sim_window_curx, sim_window_cury;
static INTSML        sim_window_textsize;
static INTSML        sim_window_lines;
static INTSML        sim_window_vislines;
static INTSML        sim_window_topline;
static INTSML        sim_window_txthei;
static INTSML        sim_window_offx, sim_window_offy;
static INTSML        sim_window_basex, sim_window_basey;
       INTBIG        sim_window_state;		/* cached key for "SIM_window_state" */
static float         sim_window_maincursor, sim_window_extensioncursor;
static float         sim_window_mintime, sim_window_maxtime;
static float         sim_window_analow, sim_window_anahigh, sim_window_anarange;
static float         sim_window_anadislow, sim_window_anadishigh, sim_window_anadisrange;
static DISCHANNEL   *sim_window_highlightedtrace;
static DISCHANNEL   *sim_window_plot;
static DISCHANNEL   *sim_window_plotfree;
static DISCHANNEL   *sim_window_loop;
static DISCHANNEL   *sim_window_loop2;
static INTSML        sim_colorstrengthoff;		/* color of off-strength */
static INTSML        sim_colorstrengthnode;		/* color of node-strength */
static INTSML        sim_colorstrengthgate;		/* color of gate-strength */
static INTSML        sim_colorstrengthpower;	/* color of power-strength */
static INTSML        sim_colorlevellow;			/* color of low-levels */
static INTSML        sim_colorlevelhigh;		/* color of high-levels */
static INTSML        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 */

/* prototypes for local routines */
void   sim_window_addsegment(WINDOWPART*, INTBIG*, INTBIG*, INTSML, INTSML, INTSML, INTSML);
INTSML sim_window_verticalslider(INTBIG x, INTBIG y);
INTSML sim_window_horizontalslider(INTBIG x, INTBIG y);
void   sim_window_buttonhandler(WINDOWPART*, INTSML, INTSML, INTSML);
void   sim_window_drawbox(WINDOWPART*, INTSML, INTSML, INTSML, INTSML);
void   sim_window_drawcstring(WINDOWPART*, char*);
void   sim_window_drawcursors(WINDOWPART*);
void   sim_window_drawgraph(WINDOWPART*);
void   sim_window_drawlstring(WINDOWPART*, char*, INTSML);
void   sim_window_drawrstring(WINDOWPART*, char*);
void   sim_window_drawtimescale(WINDOWPART*);
void   sim_window_drawto(WINDOWPART*, INTSML, INTSML);
void   sim_window_drawtracedrag(WINDOWPART*, INTSML on);
INTSML sim_window_eachbdown(INTBIG, INTBIG);
INTSML sim_window_eachdown(INTBIG x, INTBIG y);
INTSML sim_window_eachwdown(INTBIG, INTBIG);
void   sim_window_highlighttrace(WINDOWPART*, DISCHANNEL *tr, INTSML color);
void   sim_window_mapcoord(WINDOWPART*, INTSML*, INTSML*);
void   sim_window_moveto(INTSML, INTSML);
void   sim_window_redisphandler(WINDOWPART*);
void   sim_window_scaletowindow(WINDOWPART*, INTBIG*, INTBIG*);
void   sim_window_setcolor(INTSML);
void   sim_window_setmask(INTSML);
void   sim_window_setviewport(WINDOWPART*);
INTBIG sim_window_timetoxpos(float);
void   sim_window_writetracename(WINDOWPART*, DISCHANNEL*);
float  sim_window_xpostotime(INTBIG);
WINDOWPART *sim_findwaveformwindow(void);
WINDOWPART *sim_findschematicswindow(void);

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

/*
 * routine to initialize the simulation window
 */
void sim_window_init(void)
{
	sim_window_mintime = 0.0;
	sim_window_maxtime = 0.0000005f;
	sim_window_maincursor = 0.0000002f;
	sim_window_extensioncursor = 0.0000003f;
	sim_window_lines = 0;
	sim_window_vislines = 0;
	sim_window_topline = 0;
	sim_window_plot = NODISCHANNEL;
	sim_window_plotfree = NODISCHANNEL;
	sim_window_highlightedtrace = NODISCHANNEL;
	(void)setvalkey((INTBIG)sim_aid, VAID, sim_window_state,
		FULLSTATE | SHOWWAVEFORM, VINTEGER|VDONTSAVE);
	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();

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

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

	/* sorry, can't have more than 1 simulation window at a time */
	wavewin = sim_findwaveformwindow();
	schemwin = sim_findschematicswindow();
	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(1);
	}

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

	/* determine the number of visible signals */
	sim_window_vislines = sim_window_lines;
	if (sim_window_lines > 20) sim_window_vislines = 20;

	/* create waveform window if requested */
	if (charhandlerwave != 0)
	{
		if (wavewin == NOWINDOWPART)
		{
			wavewin = (WINDOWPART *)askaid(us_aid, "window-horiz-new");
			if (wavewin == NOWINDOWPART) return(1);
		}
		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(0);
}

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

	/* disable simulation control in schematics/layout window */
	schemwin = sim_findschematicswindow();
	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_findwaveformwindow();
	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"
 */
INTSML sim_window_isactive(NODEPROTO **np)
{
	REGISTER INTSML activity;
	REGISTER WINDOWPART *wavewin, *schemwin;

	*np = NONODEPROTO;
	activity = 0;
	schemwin = sim_findschematicswindow();
	if (schemwin != NOWINDOWPART)
	{
		activity |= SIMWINDOWSCHEMATIC;
		*np = schemwin->curnodeproto;
	}
	wavewin = sim_findwaveformwindow();
	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_findwaveformwindow();
	if (wavewin == NOWINDOWPART) return;
	sim_window_redisphandler(wavewin);
}

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

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

/*
 * routine that changes the number of visible trace lines in the simulation window to "count"
 */
void sim_window_setvislines(INTSML count)
{
	if (count <= 0) count = 1;
	sim_window_vislines = count;
}

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

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

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

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

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

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

	/* put in active list, load data */
	tr->nextdischannel = sim_window_plot;
	sim_window_plot = tr;
	(void)allocstring(&tr->name, name, sim_aid->cluster);
	tr->nodeptr = userdata;
	tr->position = position;
	tr->numsteps = 0;
	tr->nameoff = 0;
	return((INTBIG)tr);
}

/*
 * 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, INTSML count, float *time, INTSML *state)
{
	REGISTER INTSML 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 = (float *)emalloc(wanted * (sizeof (float)), sim_aid->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 = (INTSML *)emalloc(wanted * SIZEOFINTSML, sim_aid->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->status = 0;
	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, INTSML count, float *time, float *value)
{
	REGISTER INTSML 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 = (float *)emalloc(wanted * (sizeof (float)), sim_aid->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_aid->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->status = RED;
	tr->numsteps = count;
}

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

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

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

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

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

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

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

	tr = (DISCHANNEL *)tri;
	if (tr == NODISCHANNEL || tr == 0) return;
	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);
}

/*
 * routine to delete all traces
 */
void sim_window_killalltraces(void)
{
	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);
	}
	sim_window_plot = NODISCHANNEL;
	sim_window_highlightedtrace = NODISCHANNEL;
}

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

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

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

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

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

/*
 * 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, float time)
{
	REGISTER DISCHANNEL *tr;
	REGISTER INTSML j;
	REGISTER float lasttime, lastvalue, thistime, thisvalue, v;

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

	/* must be an analog trace */
	if (tr->status == 0) return(0.0);

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

	return(lastvalue);
}

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

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

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

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

void sim_window_inittraceloop(void)
{
	sim_window_loop = sim_window_plot;
}

void sim_window_inittraceloop2(void)
{
	sim_window_loop2 = sim_window_plot;
}

INTBIG sim_window_nexttraceloop(void)
{
	REGISTER DISCHANNEL *tr;

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

INTBIG sim_window_nexttraceloop2(void)
{
	REGISTER DISCHANNEL *tr;

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

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

/*
 * routine that sets trace "tri" to be highlighted in the simulation window
 */
void sim_window_sethighlighttrace(INTBIG tri)
{
	REGISTER DISCHANNEL *tr;
	REGISTER WINDOWPART *wavewin;

	tr = (DISCHANNEL *)tri;
	if (tr == 0) tr = NODISCHANNEL;

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

	if (sim_window_highlightedtrace != NODISCHANNEL)
		sim_window_highlighttrace(wavewin, sim_window_highlightedtrace, 0);
	sim_window_highlightedtrace = tr;
	if (sim_window_highlightedtrace != NODISCHANNEL)
		sim_window_highlighttrace(wavewin, sim_window_highlightedtrace, HIGHLIT);
}

/*
 * routine that returns the highlighted trace in the simulation window
 */
INTBIG sim_window_gethighlighttrace(void)
{
	if (sim_window_highlightedtrace == NODISCHANNEL) return(0);
	return((INTBIG)sim_window_highlightedtrace);
}

/*
 * 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, addr, fx, fy, tx, ty, windowstate;
	REGISTER INTSML texture;
	REGISTER VARIABLE *var;
	REGISTER NODEPROTO *simnt;
	REGISTER WINDOWPART *schemwin;
	static POLYGON *poly = NOPOLYGON;
	void us_wanttodraw(INTBIG, INTBIG, INTBIG, INTBIG, WINDOWPART*, GRAPHICS*, INTSML);

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

	/* get window state information */
	var = getvalkey((INTBIG)sim_aid, VAID, VINTEGER, sim_window_state);
	if (var == NOVARIABLE) windowstate = 0; else windowstate = var->addr;

	/* 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)
	{
		/* can only handle digital waveforms right now */
		if (tr->status != 0) 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 = getnetwork(tr->name, simnt);
		if (net == NONETWORK)
		{
			if (tr->name[0] == 'N' && tr->name[1] == 'E' && tr->name[2] == 'T')
			{
				addr = atoi(&tr->name[3]);
				for(net = simnt->firstnetwork; net != NONETWORK; net = net->nextnetwork)
					if (net == (NETWORK *)addr) break;
			}
		}
		if (net == NONETWORK)
		{
			/* check for prefix "v(" and "l(" HSPICE prefixes */
			if ((tr->name[0] == 'l' || tr->name[0] == 'v') && tr->name[1] == '(')
				net = getnetwork(&tr->name[2], simnt);
		}
		if (net != NONETWORK && j < tr->numsteps) net->temp1 = tr->statearray[j];
	}

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

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

		if ((windowstate&FULLSTATE) != 0)
		{
			/* 12-state display: determine trace texture */
			/* determine trace texture */
			switch (net->temp1 & 0377)
			{
				case NODE_STRENGTH: texture = 1;    break;
				case GATE_STRENGTH: texture = 0;    break;
				case VDD_STRENGTH:  texture = 2;    break;
				default:            texture = -1;   break;
			}

			/* 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(float time)
{
	REGISTER WINDOWPART *wavewin;

	/* get the waveform window */
	wavewin = sim_findwaveformwindow();
	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
 */
float 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(float time)
{
	REGISTER WINDOWPART *wavewin;

	/* get the waveform window */
	wavewin = sim_findwaveformwindow();
	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
 */
float sim_window_getextensioncursor(void)
{
	return(sim_window_extensioncursor);
}

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

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

/*
 * routine to return the range of time that is in the data
 */
void sim_window_gettimeextents(float *min, float *max)
{
	REGISTER DISCHANNEL *tr;
	float 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)
	{
		for(j=0; j<tr->numsteps; j++)
		{
			time = tr->timearray[j];
			if (time < *min) *min = time;
			if (time > *max) *max = time;
		}
	}
}

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

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

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

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

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

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

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

INTBIG sim_window_tracedragyoff, sim_window_initialy;
DISCHANNEL *sim_window_tracedragtr;

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

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

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

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

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

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

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

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

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

	if (y >= DISPLAYSLIDERSIZE) return(0);
	switch (sim_waveformsliderpart)
	{
		case 0: /* left arrow clicked */
			if (x > sim_waveformwindow->uselx + DISPLAYSLIDERSIZE) return(0);
			if (sim_window_mintime <= 0.0) break;
			range = sim_window_maxtime - sim_window_mintime;
			sim_window_mintime -= range/10.0f;
			sim_window_maxtime -= range/10.0f;
			if (sim_window_mintime < 0.0)
			{
				sim_window_maxtime -= sim_window_mintime;
				sim_window_mintime = 0.0;
			}
			sim_window_redisphandler(sim_waveformwindow);
			break;
		case 1: /* left of thumb */
			if (x > sim_waveformwindow->thumblx) return(0);
			if (sim_window_mintime <= 0.0) break;
			range = sim_window_maxtime - sim_window_mintime;
			sim_window_mintime -= range/2.0f;
			sim_window_maxtime -= range/2.0f;
			if (sim_window_mintime < 0.0)
			{
				sim_window_maxtime -= sim_window_mintime;
				sim_window_mintime = 0.0;
			}
			sim_window_redisphandler(sim_waveformwindow);
			break;
		case 2: /* on thumb (not handled here) */
			break;
		case 3: /* right of thumb */
			if (x < sim_waveformwindow->thumbhx) return(0);
			range = sim_window_maxtime - sim_window_mintime;
			sim_window_mintime += range/2.0f;
			sim_window_maxtime += range/2.0f;
			sim_window_redisphandler(sim_waveformwindow);
			break;
		case 4: /* right arrow clicked */
			if (x < sim_waveformwindow->usehx - DISPLAYSLIDERSIZE) return(0);
			range = sim_window_maxtime - sim_window_mintime;
			sim_window_mintime += range/10.0f;
			sim_window_maxtime += range/10.0f;
			sim_window_redisphandler(sim_waveformwindow);
			break;
	}
	return(0);
}

void sim_window_buttonhandler(WINDOWPART *w, INTSML button, INTSML inx, INTSML iny)
{
	INTBIG x, y, xpos, ypos, newtop;
	float newpos;
	INTSML ind, temp_trace_spacing, tracelinesmoved;
	char *par[2];
	REGISTER DISCHANNEL *tr, *otr, *trprev, *otrprev;

	ttynewcommand();

	/* hit in slider on left */
	if (inx < DISPLAYSLIDERSIZE)
	{
		if (iny <= w->usely + DISPLAYSLIDERSIZE)
		{
			/* hit in bottom arrow: go down 1 (may repeat) */
			sim_waveformwindow = w;
			sim_waveformsliderpart = 0;
			trackcursor(0, 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(0, us_nullup, us_nullvoid, sim_window_verticalslider, us_nullchar,
				us_nullvoid, TRACKNORMAL);
			return;
		}
		if (iny <= w->thumbhy)
		{
			/* hit on thumb: track thumb motion */
			us_vthumbbegin(iny, w, w->uselx-DISPLAYSLIDERSIZE, w->usely, w->usehy,
				sim_window_lines, 1);
			trackcursor(0, us_nullup, us_nullvoid, us_vthumbdown, us_nullchar,
				us_svthumbdone, TRACKNORMAL); 
			newtop = us_svthumbend();
			if (newtop >= 0)
			{
				sim_window_topline = (INTSML)newtop;
				sim_window_redisphandler(w);
			}
			return;
		}
		if (iny < w->usehy - DISPLAYSLIDERSIZE)
		{
			/* hit above thumb: go up lots (may repeat) */
			sim_waveformwindow = w;
			sim_waveformsliderpart = 3;
			trackcursor(0, 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(0, us_nullup, us_nullvoid, sim_window_verticalslider, us_nullchar,
			us_nullvoid, TRACKNORMAL);
		return;
	}

	/* hit in slider on bottom */
	if (iny < DISPLAYSLIDERSIZE)
	{
		if (inx <= w->uselx + DISPLAYSLIDERSIZE)
		{
			/* hit in left arrow: small shift down in time (may repeat) */
			sim_waveformwindow = w;
			sim_waveformsliderpart = 0;
			trackcursor(0, 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(0, us_nullup, us_nullvoid, sim_window_horizontalslider, us_nullchar,
				us_nullvoid, TRACKNORMAL);
			return;
		}
		if (inx <= w->thumbhx)
		{
			/* hit on thumb: track thumb motion */
			us_hthumbbegin(inx, w, w->usely, w->uselx, w->usehx);
			trackcursor(0, us_nullup, us_nullvoid, us_hthumbdown, us_nullchar,
				us_shthumbdone, TRACKNORMAL); 
			newpos = us_shthumbend();
			if (newpos == sim_window_mintime) return;
			sim_window_maxtime += newpos - sim_window_mintime;
			sim_window_mintime = newpos;
			sim_window_redisphandler(w);
			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(0, 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(0, us_nullup, us_nullvoid, sim_window_horizontalslider, us_nullchar,
			us_nullvoid, TRACKNORMAL);
		return;
	}

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

	/* see if a signal name was hit */
	if (x < 114)
	{
		sim_window_sethighlighttrace(0);
		temp_trace_spacing = 540 / sim_window_vislines;
		ind = sim_window_vislines - 1 - (y - 31) / temp_trace_spacing;
		trprev = NODISCHANNEL;
		for(tr = sim_window_plot; tr != NODISCHANNEL; tr = tr->nextdischannel)
		{
			if (tr->position == ind + sim_window_topline)
			{
				ypos = (sim_window_vislines - 1 - ind) * temp_trace_spacing + 31 + tr->nameoff;
				if (y >= ypos && y <= ypos + sim_window_txthei) break;
			}
			trprev = tr;
		}
		if (tr == NODISCHANNEL) return;

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

			/* see if the signal name was dragged to a new location */
			if (sim_window_tracedragyoff < 0) sim_window_tracedragyoff -= temp_trace_spacing/2; else
				sim_window_tracedragyoff += temp_trace_spacing/2;
			tracelinesmoved = sim_window_tracedragyoff / temp_trace_spacing;
			if (tracelinesmoved != 0)
			{
				/* find which signal name it was dragged to */
				otrprev = NODISCHANNEL;
				for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel)
				{
					if (otr->position == tr->position - tracelinesmoved) break;
					otrprev = otr;
				}
				if (otr != NODISCHANNEL)
				{
					/* remember number of screen lines */
					ind = sim_window_plot->position;

					/* pull out the selected signal */
					if (trprev == NODISCHANNEL) sim_window_plot = tr->nextdischannel; else
						trprev->nextdischannel = tr->nextdischannel;
					if (tr->position > otr->position)
					{
						/* shift this signal down the screen */
						tr->nextdischannel = otr->nextdischannel;
						otr->nextdischannel = tr;
					} else
					{
						/* shift this signal up the screen */
						tr->nextdischannel = otr;
						if (otrprev == NODISCHANNEL) sim_window_plot = tr; else
							otrprev->nextdischannel = tr;
					}
					for(otr = sim_window_plot; otr != NODISCHANNEL; otr = otr->nextdischannel, ind--)
						otr->position = ind;

					/* redisplay window */
					sim_window_redraw();
				}
			}
		}

		/* highlight the selected signal */
		sim_window_sethighlighttrace((INTBIG)tr);
		par[0] = "highlight";
		tellaid(net_aid, 1, par);
		return;
	}

	/* see if a cursor was hit */
	xpos = sim_window_timetoxpos(sim_window_maincursor);
	if (abs(x-xpos) < 10)
	{
		/* main cursor hit */
		trackcursor(0, 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(0, us_nullup, us_nullvoid, sim_window_eachbdown,
			us_nullchar, us_nullvoid, TRACKDRAGGING);
		return;
	}
}

/*
 * This procedure redraws the display, allowing for resize.
 */
void sim_window_redisphandler(WINDOWPART *w)
{
	INTSML lx, hx, ly, hy;
	float range;
	REGISTER INTBIG thumbtop, thumbarea, thumbsize;

	sim_window_setviewport(w);

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

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

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

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

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

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

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

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

	sim_window_drawtimescale(w);
	sim_window_drawgraph(w);
	sim_window_drawcursors(w);
	if (sim_window_highlightedtrace != NODISCHANNEL)
		sim_window_highlighttrace(w, sim_window_highlightedtrace, HIGHLIT);
}

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

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

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

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

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

/*
 * This procedure prints out the time scale on the graphics terminal.
 */
void sim_window_drawtimescale(WINDOWPART *wavewin)
{
	INTBIG i, x_pos;
	float time;
	char s2[20];

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

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

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

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

	/* set the text color */
	if (tr->status == 0) sim_window_setcolor(MENTXT); else
		sim_window_setcolor(tr->status);

	/* write the text */
	px = 1;   py = y_pos + tr->nameoff;
	maxheight = applyyscale(wavewin, temp_trace_spacing);
	maxwidth = applyxscale(wavewin, 111);
	sim_window_mapcoord(wavewin, &px, &py);
	len = strlen(tr->name);
	for(size = sim_window_textsize; size >= TXT4P; size--)
	{
		screensettextsize(wavewin, size);
		screengettextsize(wavewin, tr->name, &wid, &hei);
		if (hei > maxheight && size > TXT4P) 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;
	}
}

void sim_window_highlighttrace(WINDOWPART *wavewin, DISCHANNEL *tr, INTSML color)
{
	INTSML y_pos, temp_trace_spacing, line, lx, hx, ly, hy, slot, y_min, y_max,
		x_min, x_max, first, j, state, laststate, tx, ty, fx, fy;
	INTBIG cfx, cfy, ctx, cty;
	float time, position;

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

	sim_window_setmask(LAYERH);
	sim_window_setcolor(color);
	sim_window_moveto(lx, ly);
	sim_window_drawto(wavewin, hx, ly);
	sim_window_drawto(wavewin, hx, hy);
	sim_window_drawto(wavewin, lx, hy);
	sim_window_drawto(wavewin, lx, ly);

	slot = tr->position - sim_window_topline;
	y_min = (sim_window_vislines - 1 - slot) * temp_trace_spacing + 31;
	y_max = y_min + temp_trace_spacing - 10;

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

			state = tr->statearray[j] >> 8;
			if (first == 0 && state != laststate)
			{
				sim_window_moveto(x_min, y_min);
				sim_window_drawto(wavewin, x_min, y_max);
			}
			first = 0;
			switch (state)
			{
				case LOGIC_LOW:
					sim_window_moveto(x_min, y_min);
					sim_window_drawto(wavewin, x_max, y_min);
					break;
				case LOGIC_HIGH:
					sim_window_moveto(x_min, y_max);
					sim_window_drawto(wavewin, x_max, y_max);
					break;
				default:
					sim_window_drawbox(wavewin, x_min, y_min, x_max, y_max);
					break;
			}
			laststate = state;
		} else
		{
			/* analog waveform */
			tx = (INTSML)sim_window_timetoxpos(time);
			position = (float)(y_max - y_min);
			position *= (tr->valuearray[j] * sim_window_anarange - sim_window_anadislow) /
				sim_window_anadisrange;
			ty = (INTSML)(position + y_min);
			if (j != 0 && tx >= 114)
			{
				if (tx > 767)
				{
					/* "to" data point off the screen: interpolate */
					ty = (ty-fy) * (767-fx) / (tx-fx) + fy;
					tx = 767;
				}

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

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

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

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

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

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

void sim_window_drawgraph(WINDOWPART *wavewin)
{
	INTSML x_min, x_max, y_min, y_max, j, maxoff, state, laststate, color, first,
		slot, temp_trace_spacing, range, hasana, fx, fy, tx, ty, x, y;
	INTBIG cfx, cfy, ctx, cty;
	char s2[15];
	REGISTER DISCHANNEL *tr, *otr;
	float time, position;

	/* determine height of text in the window */
	screensettextsize(wavewin, sim_window_textsize);
	screengettextsize(wavewin, "X", &x, &y);
	sim_window_txthei = y * (wavewin->screenhy - wavewin->screenly) /
		(wavewin->usehy - wavewin->usely - 20);

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

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

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

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

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

		slot = tr->position - sim_window_topline;
		if (slot < 0 || slot >= sim_window_vislines) continue;
		sim_window_writetracename(wavewin, tr);
		y_min = (sim_window_vislines - 1 - slot) * temp_trace_spacing + 31;
		y_max = y_min + temp_trace_spacing - 10;

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

				state = tr->statearray[j] >> 8;
				switch (tr->statearray[j] & 0377)
				{
					case NODE_STRENGTH: color = sim_colorstrengthnode;    break;
					case GATE_STRENGTH: color = sim_colorstrengthgate;    break;
					case VDD_STRENGTH:  color = sim_colorstrengthpower;   break;
					default:            color = sim_colorstrengthoff;     break;
				}
				sim_window_setcolor(color);
				if (first == 0 && state != laststate)
				{
					sim_window_moveto(x_min, y_min);
					sim_window_drawto(wavewin, x_min, y_max);
				}
				first = 0;
				switch (state)
				{
					case LOGIC_LOW:
						sim_window_moveto(x_min, y_min);
						sim_window_drawto(wavewin, x_max, y_min);
						break;
					case LOGIC_X:
						sim_window_drawbox(wavewin, x_min, y_min, x_max, y_max);
						break;
					case LOGIC_HIGH:
						sim_window_moveto(x_min, y_max);
						sim_window_drawto(wavewin, x_max, y_max);
						break;
					default:
						sim_window_drawbox(wavewin, x_min, y_min, x_max, y_max);
						if ((x_max - x_min) <= 1) break;
						sim_window_setcolor(ALLOFF);
						sim_window_drawbox(wavewin, (INTSML)(x_min+1), (INTSML)(y_min+1),
							(INTSML)(x_max-1), (INTSML)(y_max-1));
						(void)sprintf(s2, "0x%X", state);
						sim_window_moveto((INTSML)((x_min + x_max) / 2), (INTSML)(y_min+3));
						sim_window_setcolor(color);
						sim_window_drawcstring(wavewin, s2);
						break;
				}
				laststate = state;
			} else
			{
				/* analog waveform */
				tx = (INTSML)sim_window_timetoxpos(time);
				position = (float)(y_max - y_min);
				position *= (tr->valuearray[j] * sim_window_anarange - sim_window_anadislow) /
					sim_window_anadisrange;
				ty = (INTSML)(position + y_min);
				if (j != 0 && tx >= 114)
				{
					if (tx > 767)
					{
						/* "to" data point off the screen: interpolate */
						ty = (ty-fy) * (767-fx) / (tx-fx) + fy;
						tx = 767;
					}

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

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

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

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

	/* determine height of text in the window */
	screensettextsize(wavewin, sim_window_textsize);
	screengettextsize(wavewin, "X", &x, &y);
	sim_window_txthei = y * (wavewin->screenhy - wavewin->screenly) /
		(wavewin->usehy - wavewin->usely - 20);

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

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

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

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

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

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

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

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

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

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

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

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

	/* plot time values */
	for(j=1; j<5; j++)
	{
		time = j * 0.2f * (sim_window_maxtime - sim_window_mintime) + sim_window_mintime;
		x = (INTSML)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) var->textdescript = (TXT10P << VTSIZESH) | VTPOSUP;
	}

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

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

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

		/* write the text */
		(void)sprintf(s2, "%g", sim_window_anahigh);
		tx1 = 1;   ty1 = y_min + tr->nameoff;   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) var->textdescript = (TXT10P << VTSIZESH) | VTPOSUPRIGHT;
		if (tr->status != 0)
		{
			tx1 = 0;     ty1 = y_min + tr->nameoff;
			sim_window_mapcoord(wavewin, &tx1, &ty1);
			tx2 = 100;   ty2 = y_min + tr->nameoff;
			sim_window_mapcoord(wavewin, &tx2, &ty2);
			data[0] = -(tx2-tx1)/2;  data[1] = 0;
			data[2] = (tx2-tx1)/2;   data[3] = 0;
			ni = newnodeinst(art_openedpolygonprim, tx1, tx2, ty1, ty2, 0, 0, plotnp);
			if (ni == NONODEINST) { efree((char *)data);  return; }
			(void)setvalkey((INTBIG)ni, VNODEINST, el_trace, (INTBIG)data,
				VINTEGER|VISARRAY|(4<<VLENGTHSH));
			(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, tr->status, VINTEGER);
		}

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

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

				switch (tr->statearray[j] & 0377)
				{
					case NODE_STRENGTH: color = sim_colorstrengthnode;    break;
					case GATE_STRENGTH: color = sim_colorstrengthgate;    break;
					case VDD_STRENGTH:  color = sim_colorstrengthpower;   break;
					default:            color = sim_colorstrengthoff;     break;
				}
				if (datapos > 0 && color != lastcolor)
				{
					/* strength has changed, dump out data so far */
					tx1 = 114;   ty1 = 31;    sim_window_mapcoord(wavewin, &tx1, &ty1);
					tx2 = 767;   ty2 = 570;   sim_window_mapcoord(wavewin, &tx2, &ty2);
					ni = newnodeinst(art_openedpolygonprim, tx1, tx2, ty1, ty2, 0, 0, plotnp);
					if (ni == NONODEINST) { efree((char *)data);  return; }
					(void)setvalkey((INTBIG)ni, VNODEINST, el_trace, (INTBIG)data,
						VINTEGER|VISARRAY|(datapos<<VLENGTHSH));
					(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, lastcolor, VINTEGER);
					datapos = 0;
				}
				lastcolor = color;

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

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

	/* 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(INTSML strength, INTSML color)
{
	char *colorname, *colorsymbol;

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

void sim_window_addsegment(WINDOWPART *wavewin, INTBIG *data, INTBIG *datapos, INTSML fx, INTSML fy,
	INTSML tx, INTSML ty)
{
	REGISTER INTSML truefx, truefy;
	REGISTER INTBIG pos;
	INTSML 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;
}

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

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

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

	scalex = (w->usehx - w->uselx) / 767.0f;
	scaley = ((w->usehy-20) - w->usely) / 600.0f;
	if (scalex > 2.0 && scaley > 2.0) sim_window_textsize = TXT20P; else
		if (scalex > 1.0 && scaley > 1.0) sim_window_textsize = TXT16P; else
			if (scalex > 0.75 && scaley > 0.75) sim_window_textsize = TXT12P; else
				sim_window_textsize = TXT8P;
}

/*
 * Routine to return the WINDOWPART that has the waveform display
 */
WINDOWPART *sim_findwaveformwindow(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_findschematicswindow(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, INTSML *x, INTSML *y)
{
	REGISTER INTSML newx, newy;

	newx = w->uselx + (INTSML)applyxscale(w, *x - w->screenlx);
	newy = w->usely + (INTSML)((*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(INTSML color)
{
	sim_window_desc.col = color;
}

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

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

void sim_window_drawto(WINDOWPART *w, INTSML x, INTSML y)
{
	INTSML fx, fy, tx, ty;
	INTBIG 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 = (INTSML)fx;   cfy = (INTSML)fy;
	ctx = (INTSML)tx;   cty = (INTSML)ty;
	if (clipline(&cfx, &cfy, &ctx, &cty, w->uselx, w->usehx, w->usely, w->usehy) == 0)
	{
		fx = (INTSML)cfx;   fy = (INTSML)cfy;
		tx = (INTSML)ctx;   ty = (INTSML)cty;
		screendrawline(w, fx, fy, tx, ty, &sim_window_desc, 0);
	}
	sim_window_curx = x;
	sim_window_cury = y;
}

void sim_window_drawbox(WINDOWPART *w, INTSML xll, INTSML yll, INTSML xur, INTSML yur)
{
	INTSML 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);
}

void sim_window_drawcstring(WINDOWPART *w, char *s)
{
	INTSML x, y, px, py;

	px = sim_window_curx;   py = sim_window_cury;
	sim_window_mapcoord(w, &px, &py);
	screensettextsize(w, sim_window_textsize);
	screengettextsize(w, s, &x, &y);
	screendrawtext(w, (INTSML)(px-x/2), py, s, &sim_window_desc);
}

void sim_window_drawlstring(WINDOWPART *w, char *s, INTSML maxx)
{
	INTSML px, py;

	px = sim_window_curx;   py = sim_window_cury;
	sim_window_mapcoord(w, &px, &py);
	screensettextsize(w, sim_window_textsize);
	screendrawtext(w, px, py, s, &sim_window_desc);
}

void sim_window_drawrstring(WINDOWPART *w, char *s)
{
	INTSML x, y, px, py;

	screensettextsize(w, sim_window_textsize);
	screengettextsize(w, s, &x, &y);
	px = sim_window_curx;   py = sim_window_cury;
	sim_window_mapcoord(w, &px, &py);
	screendrawtext(w, (INTSML)(px-x), py, s, &sim_window_desc);
}

/*
 * Name: sim_windowconvertengineeringnotation
 *
 * Description:
 *	This procedure converts a floating point number and represents time
 * in engineering units such as pico, micro, milli, etc.
 *
 * Calling Arguments:
 *	time = floating point value to be converted to Engineering notation
 *	s1   = pointer to a string that will be used to print the converted value
 */
void sim_windowconvertengineeringnotation(float time, char *s1)
{
	if (time < 10.0e-15 || time >= 9999.5)
	{
		(void)sprintf(s1, "%8.2e sec", time);
		return;
	}
	if (time >= 10.0e-15 && time < 9999.5e-15)
	{
		time = (time / 1.0e-15f) + 0.5f;
		(void)sprintf(s1, "%4dfsec", (INTSML)time);
		return;
	}
	if (time >= 1.0e-12 && time < 9999.5e-12)
	{
		time = (time / 1.0e-12f) + 0.5f;
		(void)sprintf(s1, "%4dpsec", (INTSML)time);
		return;
	}
	if (time >= 1.0e-9 && time < 9999.5e-9)
	{
		time = (time / 1.0e-9f) + 0.5f;
		(void)sprintf(s1, "%4dnsec", (INTSML)time);
		return;
	}
	if (time >= 1.0e-6 && time < 9999.5e-6)
	{
		time = (time / 1.0e-6f) + 0.5f;
		(void)sprintf(s1, "%4dusec", (INTSML)time);
		return;
	}
	if (time >= 1.0e-3 && time < 9999.5e-3)
	{
		time = (time / 1.0e-3f) + 0.5f;
		(void)sprintf(s1, "%4dmsec", (INTSML)time);
		return;
	}
	if (time >= 1.0 && time < 9999.5)
	{
		time = time + 0.5f;
		(void)sprintf(s1, "%4d sec", (INTSML)time);
		return;
	}
}

#endif  /* SIMAID - at top */
