1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: iosuei.c
6  * Input/output tool: SUE (Schematic User Environment) reader
7  * Written by: Steven M. Rubin, Static Free Software
8  *
9  * Copyright (c) 2000 Static Free Software.
10  *
11  * Electric(tm) is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * Electric(tm) is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Electric(tm); see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, Mass 02111-1307, USA.
25  *
26  * Static Free Software
27  * 4119 Alpine Road
28  * Portola Valley, California 94028
29  * info@staticfreesoft.com
30  */
31 
32 #include "config.h"
33 #include "global.h"
34 #include "edialogs.h"
35 #include "egraphics.h"
36 #include "eio.h"
37 #include "tech.h"
38 #include "tecart.h"
39 #include "tecgen.h"
40 #include "tecschem.h"
41 #include "network.h"
42 #include "usr.h"
43 
44 /* #define SHOWORIGINAL 1 */		/* uncomment to show original nodes */
45 
46 #define E0     (WHOLE/8)	/*  0.125 */
47 #define S1     (T1+E0)		/*  1.875 */
48 #define F3     (K3+H0+E0)	/*  3.625 */
49 #define S6     (T6+E0)		/*  6.875 */
50 #define Q11    (K11+Q0)		/* 11.25 */
51 
52 /*************** SUE EQUIVALENCES ***************/
53 
54 typedef struct
55 {
56 	CHAR   *portname;
57 	INTBIG  xoffset;
58 	INTBIG  yoffset;
59 } SUEEXTRAWIRE;
60 
61 typedef struct
62 {
63 	CHAR         *suename;
64 	NODEPROTO   **intproto;
65 	INTBIG        netateoutput;
66 	INTBIG        rotation;
67 	INTBIG        transpose;
68 	INTBIG        xoffset;
69 	INTBIG        yoffset;
70 	INTBIG        detailbits;
71 	INTBIG        numextrawires;
72 	SUEEXTRAWIRE *extrawires;
73 } SUEEQUIV;
74 
75 SUEEXTRAWIRE io_suetransistorwires[] =
76 {
77 	{x_("d"),    K3,   0},
78 	{x_("s"),   -K3,   0},
79 	{x_("g"),     0,  H4}
80 };
81 
82 SUEEXTRAWIRE io_suetransistor4wires[] =
83 {
84 	{x_("d"),    K3,   0},
85 	{x_("s"),   -K3,   0},
86 	{x_("b"),   -Q0, -H2},
87 	{x_("g"),     0,  H4}
88 };
89 
90 SUEEXTRAWIRE io_sueresistorwires[] =
91 {
92 	{x_("a"),   -K3,   0},
93 	{x_("b"),    K3,   0}
94 };
95 
96 SUEEXTRAWIRE io_suecapacitorwires[] =
97 {
98 	{x_("a"),     0,  T1},
99 	{x_("b"),     0, -T1}
100 };
101 
102 SUEEXTRAWIRE io_suesourcewires[] =
103 {
104 	{x_("minus"), 0, -Q1},
105 	{x_("plus"),  0,  H1}
106 };
107 
108 SUEEXTRAWIRE io_suetwoportwires[] =
109 {
110 	{x_("a"),  -Q11,  F3},
111 	{x_("b"),  -Q11, -F3},
112 	{x_("x"),   Q11,  F3},
113 	{x_("y"),   Q11, -F3}
114 };
115 
116 SUEEQUIV io_sueequivs[] =
117 {
118 	/* name         primitive           NEG  ANG     X   Y  BITS         WIR EXTRA-WIRES */
119 	{x_("pmos10"),     &sch_transistorprim,  0, 900,0, -K2,  0, TRANPMOS,     3, io_suetransistorwires},
120 	{x_("nmos10"),     &sch_transistorprim,  0, 900,0, -K2,  0, TRANNMOS,     3, io_suetransistorwires},
121 	{x_("pmos4"),      &sch_transistorprim,  0, 900,0, -K2,  0, TRANPMOS,     3, io_suetransistorwires},
122 	{x_("nmos4"),      &sch_transistorprim,  0, 900,0, -K2,  0, TRANNMOS,     3, io_suetransistorwires},
123 	{x_("pmos"),       &sch_transistorprim,  0, 900,0, -K2,  0, TRANPMOS,     3, io_suetransistorwires},
124 	{x_("nmos"),       &sch_transistorprim,  0, 900,0, -K2,  0, TRANNMOS,     3, io_suetransistorwires},
125 	{x_("capacitor"),  &sch_capacitorprim,   0,   0,0,   0,  0, 0,            2, io_suecapacitorwires},
126 	{x_("resistor"),   &sch_resistorprim,    0, 900,0,   0,  0, 0,            2, io_sueresistorwires},
127 	{x_("inductor"),   &sch_inductorprim,    0,   0,0,   0,  0, 0,            0, 0},
128 	{x_("cccs"),       &sch_twoportprim,     0,   0,0,  Q1,-S6, TWOPCCCS,     4, io_suetwoportwires},
129 	{x_("ccvs"),       &sch_twoportprim,     0,   0,0,  Q1,-S6, TWOPCCVS,     4, io_suetwoportwires},
130 	{x_("vcvs"),       &sch_twoportprim,     0,   0,0,  Q1,-S6, TWOPVCVS,     4, io_suetwoportwires},
131 	{x_("vccs"),       &sch_twoportprim,     0,   0,0, -S1,-K5, TWOPVCCS,     0, 0},
132 	{0, 0, 0, 0, 0, 0, 0, 0, 0}
133 };
134 
135 SUEEQUIV io_sueequivs4[] =
136 {
137 	/* name         primitive           NEG  ANG     X   Y  BITS         WIR EXTRA-WIRES */
138 	{x_("pmos10"),     &sch_transistor4prim, 0,   0,1, -K2,  0, TRANPMOS,     4, io_suetransistor4wires},
139 	{x_("nmos10"),     &sch_transistor4prim, 0, 900,0, -K2,  0, TRANNMOS,     4, io_suetransistor4wires},
140 	{x_("pmos4"),      &sch_transistor4prim, 0,   0,1, -K2,  0, TRANPMOS,     4, io_suetransistor4wires},
141 	{x_("nmos4"),      &sch_transistor4prim, 0, 900,0, -K2,  0, TRANNMOS,     4, io_suetransistor4wires},
142 	{x_("pmos"),       &sch_transistor4prim, 0,   0,1, -K2,  0, TRANPMOS,     4, io_suetransistor4wires},
143 	{x_("nmos"),       &sch_transistor4prim, 0, 900,0, -K2,  0, TRANNMOS,     4, io_suetransistor4wires},
144 	{x_("capacitor"),  &sch_capacitorprim,   0,   0,0,   0,  0, 0,            2, io_suecapacitorwires},
145 	{x_("resistor"),   &sch_resistorprim,    0, 900,0,   0,  0, 0,            2, io_sueresistorwires},
146 	{x_("inductor"),   &sch_inductorprim,    0,   0,0,   0,  0, 0,            0, 0},
147 	{x_("cccs"),       &sch_twoportprim,     0,   0,0,  Q1,-S6, TWOPCCCS,     4, io_suetwoportwires},
148 	{x_("ccvs"),       &sch_twoportprim,     0,   0,0,  Q1,-S6, TWOPCCVS,     4, io_suetwoportwires},
149 	{x_("vcvs"),       &sch_twoportprim,     0,   0,0,  Q1,-S6, TWOPVCVS,     4, io_suetwoportwires},
150 	{x_("vccs"),       &sch_twoportprim,     0,   0,0, -S1,-K5, TWOPVCCS,     0, 0},
151 	{0, 0, 0, 0, 0, 0, 0, 0, 0}
152 };
153 
154 /*************** SUE WIRES ***************/
155 
156 #define NOSUEWIRE ((SUEWIRE *)-1)
157 
158 typedef struct Isuewire
159 {
160 	INTBIG           x[2], y[2];
161 	NODEINST        *ni[2];
162 	PORTPROTO       *pp[2];
163 	ARCPROTO        *proto;
164 	struct Isuewire *nextsuewire;
165 } SUEWIRE;
166 
167 SUEWIRE *io_suefreewire = NOSUEWIRE;
168 
169 /*************** SUE NETWORKS ***************/
170 
171 #define NOSUENET ((SUENET *)-1)
172 
173 typedef struct Isuenet
174 {
175 	INTBIG          x, y;
176 	CHAR           *label;
177 	struct Isuenet *nextsuenet;
178 } SUENET;
179 
180 SUENET *io_suefreenet = NOSUENET;
181 
182 /*************** MISCELLANEOUS ***************/
183 
184 #define MAXLINE       300			/* maximum characters on an input line */
185 #define MAXKEYWORDS    50			/* maximum keywords on an input line */
186 #define MAXICONPOINTS  25			/* maximum coordinates on an "icon_line" line */
187 
188 static INTBIG io_suelineno;
189 static INTBIG io_suefilesize;
190 static CHAR   io_suelastline[MAXLINE];
191 static CHAR   io_sueorigline[MAXLINE];
192 static CHAR   io_suecurline[MAXLINE];
193 static CHAR **io_suedirectories;
194        INTBIG io_suenumdirectories = 0;
195 static FILE  *io_suefilein;
196 
197 /* prototypes for local routines */
198 static BOOLEAN    io_sueadddirectory(CHAR *directory);
199 static void       io_suecleardirectories(void);
200 static CHAR      *io_suefindbusname(ARCINST *ai);
201 static BOOLEAN    io_suefindnode(INTBIG *x, INTBIG *y, INTBIG ox, INTBIG oy, NODEPROTO *np,
202 					NODEINST **ni, PORTPROTO **pp, NODEINST *notthisnode, INTBIG lambda);
203 static NODEINST  *io_suefindpinnode(INTBIG x, INTBIG y, NODEPROTO *np, PORTPROTO **thepp);
204 static void       io_suefreenets(SUENET *firstsn);
205 static void       io_suefreewires(SUEWIRE *firstsw);
206 static INTBIG     io_suegetnextline(CHAR **keywords, INTBIG curlydepth, void *dia);
207 static NODEPROTO *io_suegetnodeproto(LIBRARY *lib, CHAR *protoname);
208 static void       io_suekillnet(SUENET *sn);
209 static void       io_suekillwire(SUEWIRE *sw);
210 static INTBIG     io_suemakex(INTBIG x, INTBIG lambda);
211 static INTBIG     io_suemakey(INTBIG y, INTBIG lambda);
212 static PORTPROTO *io_suenewexport(NODEPROTO *cell, NODEINST *ni, PORTPROTO *pp, CHAR *thename);
213 static SUENET    *io_suenewnet(void);
214 static SUEWIRE   *io_suenewwire(void);
215 static void       io_sueplacenets(SUENET *firstsuenet, NODEPROTO *cell);
216 static CHAR      *io_sueparseexpression(CHAR *expression);
217 static void       io_sueparseparameters(CHAR **keywords, INTBIG count, INTBIG *x, INTBIG *y, INTBIG lambda,
218 					INTBIG *rot, INTBIG *trn, INTBIG *type, CHAR **thename, CHAR **thelabel, CHAR **thetext,
219 					void *dia);
220 static void       io_sueplacewires(SUEWIRE *firstsuewire, SUENET *firstsuenet, NODEPROTO *cell, INTBIG lambda);
221 static NODEPROTO *io_suereadfile(LIBRARY *lib, CHAR *cellname, void *dia);
222 static NODEPROTO *io_suereadfromdisk(LIBRARY *lib, CHAR *name, void *dia);
223 static CHAR      *io_suesearchbusname(ARCINST *ai);
224 static PORTPROTO *io_suewiredport(NODEINST *ni, INTBIG *x, INTBIG *y, INTBIG ox, INTBIG oy);
225 
226 /*
227  * Routine to free all memory associated with this module.
228  */
io_freesuememory(void)229 void io_freesuememory(void)
230 {
231 	SUEWIRE *sw;
232 	SUENET *sn;
233 
234 	while (io_suefreewire != NOSUEWIRE)
235 	{
236 		sw = io_suefreewire;
237 		io_suefreewire = io_suefreewire->nextsuewire;
238 		efree((CHAR *)sw);
239 	}
240 	while (io_suefreenet != NOSUENET)
241 	{
242 		sn = io_suefreenet;
243 		io_suefreenet = io_suefreenet->nextsuenet;
244 		efree((CHAR *)sn);
245 	}
246 	io_suecleardirectories();
247 }
248 
io_readsuelibrary(LIBRARY * lib)249 BOOLEAN io_readsuelibrary(LIBRARY *lib)
250 {
251 	REGISTER INTBIG len, i, filecount;
252 	REGISTER NODEPROTO *topcell;
253 	CHAR cellname[300], dirname[300], topdirname[300], truesuefile[300],
254 		*filename, **filelist;
255 	void *dia;
256 
257 	/* open the file */
258 	io_suefilein = xopen(lib->libfile, io_filetypesue, x_(""), &filename);
259 	if (io_suefilein == 0)
260 	{
261 		ttyputerr(_("File %s not found"), lib->libfile);
262 		return(TRUE);
263 	}
264 
265 	/* determine the cell name */
266 	estrcpy(truesuefile, filename);
267 	estrcpy(cellname, filename);
268 	len = estrlen(cellname);
269 	if (namesame(&cellname[len-4], x_(".sue")) == 0)
270 		cellname[len-4] = 0;
271 	for(i = estrlen(cellname)-1; i>0; i--)
272 		if (cellname[i] == DIRSEP) break;
273 	i++;
274 	estrcpy(cellname, &cellname[i]);
275 
276 	/* initialize the number of directories that need to be searched */
277 	io_suecleardirectories();
278 
279 	/* determine the current directory */
280 	estrcpy(topdirname, filename);
281 	len = estrlen(topdirname);
282 	for(i = len-1; i>0; i--)
283 		if (topdirname[i] == DIRSEP) break;
284 	topdirname[i+1] = 0;
285 	if (io_sueadddirectory(topdirname)) return(TRUE);
286 
287 	/* find all subdirectories that start with "suelib_" and include them in the search */
288 	filecount = filesindirectory(topdirname, &filelist);
289 	for(i=0; i<filecount; i++)
290 	{
291 		if (namesamen(filelist[i], x_("suelib_"), 7) != 0) continue;
292 		estrcpy(dirname, topdirname);
293 		estrcat(dirname, filelist[i]);
294 		if (fileexistence(dirname) != 2) continue;
295 		estrcat(dirname, DIRSEPSTR);
296 		if (io_sueadddirectory(dirname)) return(TRUE);
297 	}
298 
299 	/* see if the current directory is inside of a SUELIB */
300 	len = estrlen(topdirname);
301 	for(i = len-2; i>0; i--)
302 		if (topdirname[i] == DIRSEP) break;
303 	i++;
304 	if (namesamen(&topdirname[i], x_("suelib_"), 7) == 0)
305 	{
306 		topdirname[i] = 0;
307 		filecount = filesindirectory(topdirname, &filelist);
308 		for(i=0; i<filecount; i++)
309 		{
310 			if (namesamen(filelist[i], x_("suelib_"), 7) != 0) continue;
311 			estrcpy(dirname, topdirname);
312 			estrcat(dirname, filelist[i]);
313 			if (fileexistence(dirname) != 2) continue;
314 			estrcat(dirname, DIRSEPSTR);
315 			if (io_sueadddirectory(dirname)) return(TRUE);
316 		}
317 	}
318 
319 	/* prepare for input */
320 	io_suefilesize = filesize(io_suefilein);
321 	dia = DiaInitProgress(_("Reading SUE file..."), 0);
322 	if (dia == 0)
323 	{
324 		xclose(io_suefilein);
325 		return(TRUE);
326 	}
327 	io_suelineno = 0;
328 
329 	/* read the file */
330 	DiaSetProgress(dia, 0, io_suefilesize);
331 	topcell = io_suereadfile(lib, cellname, dia);
332 	if (topcell != NONODEPROTO)
333 		lib->curnodeproto = topcell;
334 	(void)asktool(net_tool, x_("total-re-number"));
335 
336 	/* clean up */
337 	DiaDoneProgress(dia);
338 	xclose(io_suefilein);
339 	ttyputmsg(_("%s read"), truesuefile);
340 
341 	return(FALSE);
342 }
343 
344 /*
345  * Routine to read the SUE file in "f"/
346  */
io_suereadfile(LIBRARY * lib,CHAR * cellname,void * dia)347 NODEPROTO *io_suereadfile(LIBRARY *lib, CHAR *cellname, void *dia)
348 {
349 	CHAR *keywords[MAXKEYWORDS], *thename, *thelabel, *thetext, *namestring;
350 	CHAR suevarname[200], *pt, *cpt, *startkey, save;
351 	CHAR **argnames, **argvalues, **newargnames, **newargvalues;
352 	INTBIG outline[MAXICONPOINTS*2], x, y, type, dx, dy, px, py, pinx, piny, xoff, yoff,
353 		numargs, namestrlen, rot, trn, rotation, transpose, *curstate;
354 	REGISTER INTBIG count, i, j, lx, hx, ly, hy, curly, xshrink, yshrink,
355 		varcount, varindex, varoffset, pos, invertoutput, lambda, detailbits,
356 		keycount, cx, cy, p1x, p1y, p2x, p2y, start, extent, numextrawires, len,
357 		iconx, icony, newaddr, newtype, xpos, ypos, numericlambda, bits;
358 	REGISTER BOOLEAN halvesize, placeicon, varissize, isparam;
359 	XARRAY trans;
360 	double rextent, rstart;
361 	float f;
362 	REGISTER NODEPROTO *cell, *schemcell, *iconcell, *proto, *np, *cnp;
363 	REGISTER PORTPROTO *pp, *ppt;
364 	PORTPROTO *npp;
365 	REGISTER VARIABLE *var;
366 	REGISTER TECHNOLOGY *inischemtech;
367 	REGISTER NODEINST *ni;
368 	NODEINST *nni;
369 	REGISTER ARCINST *ai;
370 	REGISTER SUEWIRE *sw, *firstsuewire;
371 	REGISTER SUENET *sn, *firstsuenet;
372 	REGISTER SUEEXTRAWIRE *extrawires;
373 	REGISTER SUEEQUIV *curequivs;
374 	REGISTER void *infstr;
375 
376 	firstsuewire = NOSUEWIRE;
377 	firstsuenet = NOSUENET;
378 	schemcell = iconcell = cell = NONODEPROTO;
379 	io_suelastline[0] = 0;
380 	numargs = 0;
381 	namestrlen = 0;
382 	lambda = lib->lambda[sch_tech->techindex];
383 	inischemtech = defschematictechnology(el_curtech);
384 	numericlambda = lib->lambda[inischemtech->techindex];
385 	for(;;)
386 	{
387 	   /* get the next line of text */
388 		count = io_suegetnextline(keywords, 0, dia);
389 		if (count < 0) break;
390 		if (count == 0) continue;
391 
392 		/* handle "proc" for defining views */
393 		if (namesame(keywords[0], x_("proc")) == 0)
394 		{
395 			/* write any wires from the last proc */
396 			if (cell != NONODEPROTO)
397 			{
398 				io_sueplacewires(firstsuewire, firstsuenet, cell, lambda);
399 				io_suefreewires(firstsuewire);
400 				io_sueplacenets(firstsuenet, cell);
401 				io_suefreenets(firstsuenet);
402 				firstsuewire = NOSUEWIRE;
403 				firstsuenet = NOSUENET;
404 			}
405 
406 			if (count < 2)
407 			{
408 				ttyputerr(_("Cell %s, line %ld: 'proc' is missing arguments: %s"),
409 					cellname, io_suelineno, io_sueorigline);
410 				continue;
411 			}
412 
413 			if (namesamen(keywords[1], x_("SCHEMATIC_"), 10) == 0)
414 			{
415 				/* create the schematic cell */
416 				infstr = initinfstr();
417 				if (namesame(&keywords[1][10], x_("[get_file_name]")) == 0)
418 					addstringtoinfstr(infstr, cellname); else
419 						addstringtoinfstr(infstr, &keywords[1][10]);
420 				addstringtoinfstr(infstr, x_("{sch}"));
421 				schemcell = cell = us_newnodeproto(returninfstr(infstr), lib);
422 				placeicon = FALSE;
423 			} else if (namesamen(keywords[1], x_("ICON_"), 5) == 0)
424 			{
425 				/* create the icon cell */
426 				infstr = initinfstr();
427 				if (namesame(&keywords[1][5], x_("[get_file_name]")) == 0)
428 					addstringtoinfstr(infstr, cellname); else
429 						addstringtoinfstr(infstr, &keywords[1][5]);
430 				addstringtoinfstr(infstr, x_("{ic}"));
431 				iconcell = cell = us_newnodeproto(returninfstr(infstr), lib);
432 			} else
433 			{
434 				ttyputerr(_("Cell %s, line %ld: unknown 'proc' statement: %s"),
435 					cellname, io_suelineno, io_sueorigline);
436 			}
437 			continue;
438 		}
439 
440 		/* handle "make" for defining components */
441 		if (namesame(keywords[0], x_("make")) == 0)
442 		{
443 			if (count < 2)
444 			{
445 				ttyputerr(_("Cell %s, line %ld: 'make' is missing arguments: %s"),
446 					cellname, io_suelineno, io_sueorigline);
447 				continue;
448 			}
449 
450 			/* extract parameters */
451 			io_sueparseparameters(&keywords[2], count-2, &x, &y, lambda, &rot, &trn,
452 				&type, &thename, &thelabel, &thetext, dia);
453 
454 			/* save the name string */
455 			if (thename != 0)
456 			{
457 				len = estrlen(thename) + 1;
458 				if (len > namestrlen)
459 				{
460 					/* LINTED "namestring" used in proper order */
461 					if (namestrlen > 0) efree(namestring);
462 					namestring = (CHAR *)emalloc(len * SIZEOFCHAR, el_tempcluster);
463 					namestrlen = len;
464 				}
465 				estrcpy(namestring, thename);
466 				thename = namestring;
467 			}
468 
469 			/* ignore self-references */
470 			if (namesame(keywords[1], cellname) == 0)
471 			{
472 				if (x != 0 || y != 0)
473 				{
474 					/* queue icon placement */
475 					iconx = x;   icony = y;
476 					placeicon = TRUE;
477 				}
478 				continue;
479 			}
480 
481 			/* special case for network names: queue them */
482 			if (namesame(keywords[1], x_("name_net_m")) == 0 ||
483 				namesame(keywords[1], x_("name_net_s")) == 0 ||
484 				namesame(keywords[1], x_("name_net")) == 0)
485 			{
486 				sn = io_suenewnet();
487 				sn->x = x;
488 				sn->y = y;
489 				(void)allocstring(&sn->label, thename, io_tool->cluster);
490 				sn->nextsuenet = firstsuenet;
491 				firstsuenet = sn;
492 				continue;
493 			}
494 
495 			/* first check for special names */
496 			proto = NONODEPROTO;
497 			invertoutput = 0;
498 			rotation = transpose = 0;
499 			xoff = yoff = 0;
500 			xshrink = yshrink = 0;
501 			detailbits = 0;
502 			numextrawires = 0;
503 			extrawires = 0;
504 			type = 0;
505 			if (namesame(keywords[1], x_("inout")) == 0)
506 			{
507 				proto = sch_offpageprim;
508 				makeangle(rot, trn, trans);
509 				xform(K2, 0, &xoff, &yoff, trans);
510 				xoff = muldiv(xoff, lambda, WHOLE);
511 				yoff = muldiv(yoff, lambda, WHOLE);
512 				type = BIDIRPORT;
513 			} else if (namesame(keywords[1], x_("input")) == 0)
514 			{
515 				proto = sch_offpageprim;
516 				makeangle(rot, trn, trans);
517 				xform(-K2, 0, &xoff, &yoff, trans);
518 				xoff = muldiv(xoff, lambda, WHOLE);
519 				yoff = muldiv(yoff, lambda, WHOLE);
520 				type = INPORT;
521 			} else if (namesame(keywords[1], x_("output")) == 0)
522 			{
523 				proto = sch_offpageprim;
524 				makeangle(rot, trn, trans);
525 				xform(K2, 0, &xoff, &yoff, trans);
526 				xoff = muldiv(xoff, lambda, WHOLE);
527 				yoff = muldiv(yoff, lambda, WHOLE);
528 				type = OUTPORT;
529 			} else if (namesame(keywords[1], x_("rename_net")) == 0)
530 			{
531 				proto = sch_wirepinprim;
532 			} else if (namesame(keywords[1], x_("global")) == 0)
533 			{
534 				if (net_buswidth(thename) > 1) proto = sch_buspinprim; else
535 				{
536 					proto = sch_wirepinprim;
537 					if (namesame(thename, x_("gnd")) == 0)
538 					{
539 						makeangle(rot, trn, trans);
540 						xform(0, -K2, &xoff, &yoff, trans);
541 						xoff = muldiv(xoff, lambda, WHOLE);
542 						yoff = muldiv(yoff, lambda, WHOLE);
543 						proto = sch_gndprim;
544 						type = GNDPORT;
545 					}
546 					if (namesame(thename, x_("vdd")) == 0)
547 					{
548 						proto = sch_pwrprim;
549 						type = PWRPORT;
550 					}
551 				}
552 			} else if (namesame(keywords[1], x_("join_net")) == 0)
553 			{
554 				proto = sch_wireconprim;
555 				xshrink = -K2;
556 				makeangle(rot, trn, trans);
557 				xform(Q1, 0, &xoff, &yoff, trans);
558 				xoff = muldiv(xoff, lambda, WHOLE);
559 				yoff = muldiv(yoff, lambda, WHOLE);
560 			}
561 
562 			/* now check for internal associations to known primitives */
563 			if (proto == NONODEPROTO)
564 			{
565 				curstate = io_getstatebits();
566 				if ((curstate[1]&SUEUSE4PORTTRANS) != 0) curequivs = io_sueequivs4; else
567 					curequivs = io_sueequivs;
568 				for(i=0; curequivs[i].suename != 0; i++)
569 					if (namesame(keywords[1], curequivs[i].suename) == 0) break;
570 				if (curequivs[i].suename != 0)
571 				{
572 					proto = *curequivs[i].intproto;
573 					invertoutput = curequivs[i].netateoutput;
574 					rotation = curequivs[i].rotation;
575 					transpose = curequivs[i].transpose;
576 					makeangle(rot, trn, trans);
577 					xform(curequivs[i].xoffset, curequivs[i].yoffset, &xoff, &yoff, trans);
578 					xoff = muldiv(xoff, lambda, WHOLE);
579 					yoff = muldiv(yoff, lambda, WHOLE);
580 
581 					if (transpose != 0)
582 					{
583 						trn = 1 - trn;
584 						rot = rotation - rot;
585 						if (rot < 0) rot += 3600;
586 					} else
587 					{
588 						rot += rotation;
589 						if (rot >= 3600) rot -= 3600;
590 					}
591 					detailbits = curequivs[i].detailbits;
592 					numextrawires = curequivs[i].numextrawires;
593 					extrawires = curequivs[i].extrawires;
594 #ifdef SHOWORIGINAL
595 					defaultnodesize(proto, &px, &py);
596 					lx = x - px/2 + xoff;   hx = lx + px;
597 					ly = y - py/2 + yoff;   hy = ly + py;
598 					ni = newnodeinst(proto, lx, hx, ly, hy, trn, rot, cell);
599 					if (ni == NONODEINST) continue;
600 					ni->userbits |= detailbits;
601 					endobjectchange((INTBIG)ni, VNODEINST);
602 					proto = NONODEPROTO;
603 					invertoutput = 0;
604 					rotation = transpose = 0;
605 					xoff = yoff = 0;
606 					detailbits = 0;
607 					numextrawires = 0;
608 					extrawires = 0;
609 #endif
610 				}
611 			}
612 
613 			/* now check for references to cells */
614 			if (proto == NONODEPROTO)
615 			{
616 				/* find node or read it from disk */
617 				proto = io_suegetnodeproto(lib, keywords[1]);
618 				if (proto == NONODEPROTO)
619 					proto = io_suereadfromdisk(lib, keywords[1], dia);
620 
621 				/* set proper offsets for the cell */
622 				if (proto != NONODEPROTO)
623 				{
624 					np = iconview(proto);
625 					if (np != NONODEPROTO) proto = np;
626 					xoff = (proto->lowx + proto->highx) / 2;
627 					yoff = (proto->lowy + proto->highy) / 2;
628 					makeangle(rot, trn, trans);
629 					xform(xoff, yoff, &xoff, &yoff, trans);
630 				}
631 			}
632 
633 			/* ignore "title" specifications */
634 			if (namesamen(keywords[1], x_("title_"), 6) == 0) continue;
635 
636 			/* stop now if SUE node is unknown */
637 			if (proto == NONODEPROTO)
638 			{
639 				ttyputmsg(_("Cannot make '%s' in cell %s"), keywords[1], describenodeproto(cell));
640 				continue;
641 			}
642 
643 			/* create the instance */
644 			defaultnodesize(proto, &px, &py);
645 			px -= muldiv(xshrink, lambda, WHOLE);
646 			py -= muldiv(yshrink, lambda, WHOLE);
647 			lx = x - px/2 + xoff;   hx = lx + px;
648 			ly = y - py/2 + yoff;   hy = ly + py;
649 			ni = newnodeinst(proto, lx, hx, ly, hy, trn, rot, cell);
650 			if (ni == NONODEINST) continue;
651 			ni->userbits |= detailbits;
652 			ni->temp1 = invertoutput;
653 			if (proto->primindex == 0 && proto->cellview == el_iconview)
654 				ni->userbits |= NEXPAND;
655 			endobjectchange((INTBIG)ni, VNODEINST);
656 			if (cell->tech == gen_tech)
657 				cell->tech = whattech(cell);
658 
659 			/* add any extra wires to the node */
660 			for(i=0; i<numextrawires; i++)
661 			{
662 				pp = getportproto(proto, extrawires[i].portname);
663 				if (pp == NOPORTPROTO) continue;
664 				portposition(ni, pp, &x, &y);
665 				makeangle(ni->rotation, ni->transpose, trans);
666 				px = muldiv(extrawires[i].xoffset, lambda, WHOLE);
667 				py = muldiv(extrawires[i].yoffset, lambda, WHOLE);
668 				xform(px, py, &dx, &dy, trans);
669 				defaultnodesize(sch_wirepinprim, &px, &py);
670 				pinx = x + dx;   piny = y + dy;
671 				nni = io_suefindpinnode(pinx, piny, cell, &npp);
672 				if (nni == NONODEINST)
673 				{
674 					lx = pinx - px/2;   hx = lx + px;
675 					ly = piny - py/2;   hy = ly + py;
676 					nni = newnodeinst(sch_wirepinprim, lx, hx, ly, hy, 0, 0, cell);
677 					if (nni == NONODEINST) continue;
678 					npp = nni->proto->firstportproto;
679 				}
680 				bits = us_makearcuserbits(sch_wirearc);
681 				if (x != pinx && y != piny) bits &= ~FIXANG;
682 				ai = newarcinst(sch_wirearc, 0, bits, ni, pp, x, y, nni, npp, pinx, piny, cell);
683 				if (ai == NOARCINST)
684 				{
685 					ttyputerr(_("Error adding extra wires to node %s"), keywords[1]);
686 					break;
687 				}
688 			}
689 
690 			/* handle names assigned to the node */
691 			if (thename != 0)
692 			{
693 				/* export a port if this is an input, output, inout */
694 				if (proto == sch_offpageprim && thename != 0)
695 				{
696 					pp = proto->firstportproto;
697 					if (namesame(keywords[1], x_("output")) == 0) pp = pp->nextportproto;
698 					ppt = io_suenewexport(cell, ni, pp, thename);
699 					if (ppt == NOPORTPROTO)
700 					{
701 						ttyputmsg(_("Cell %s, line %ld, could not create port %s"),
702 							cellname, io_suelineno, thename);
703 					} else
704 					{
705 						defaulttextdescript(ppt->textdescript, NOGEOM);
706 						defaulttextsize(1, ppt->textdescript);
707 						ppt->userbits = (ppt->userbits & ~STATEBITS) | type;
708 						endobjectchange((INTBIG)ppt, VPORTPROTO);
709 					}
710 				} else
711 				{
712 					/* just name the node */
713 					var = setvalkey((INTBIG)ni, VNODEINST, el_node_name_key, (INTBIG)thename,
714 						VSTRING|VDISPLAY);
715 					if (var != NOVARIABLE)
716 						defaulttextsize(3, var->textdescript);
717 					net_setnodewidth(ni);
718 				}
719 			}
720 
721 			/* count the variables */
722 			varcount = 0;
723 			for(i=2; i<count; i += 2)
724 			{
725 				if (keywords[i][0] != '-') continue;
726 				if (namesame(keywords[i], x_("-origin")) == 0 ||
727 					namesame(keywords[i], x_("-orient")) == 0 ||
728 					namesame(keywords[i], x_("-type")) == 0 ||
729 					namesame(keywords[i], x_("-name")) == 0) continue;
730 				varcount++;
731 			}
732 
733 			/* add variables */
734 			varindex = 1;
735 			varoffset = (ni->highy - ni->lowy) / (varcount+1);
736 			for(i=2; i<count; i += 2)
737 			{
738 				if (keywords[i][0] != '-') continue;
739 				if (namesame(keywords[i], x_("-origin")) == 0 ||
740 					namesame(keywords[i], x_("-orient")) == 0 ||
741 					namesame(keywords[i], x_("-type")) == 0 ||
742 					namesame(keywords[i], x_("-name")) == 0) continue;
743 				varissize = FALSE;
744 				halvesize = FALSE;
745 				isparam = FALSE;
746 				if (namesame(&keywords[i][1], x_("w")) == 0)
747 				{
748 					estrcpy(suevarname, x_("ATTR_width"));
749 					varissize = TRUE;
750 					xpos = 2;
751 					ypos = -4;
752 				} else if (namesame(&keywords[i][1], x_("l")) == 0)
753 				{
754 					estrcpy(suevarname, x_("ATTR_length"));
755 					varissize = TRUE;
756 					xpos = -2;
757 					ypos = -4;
758 					halvesize = TRUE;
759 				} else
760 				{
761 					esnprintf(suevarname, 200, x_("ATTR_%s"), &keywords[i][1]);
762 					for(pt = suevarname; *pt != 0; pt++) if (*pt == ' ')
763 					{
764 						ttyputmsg(_("Cell %s, line %ld, bad variable name (%s)"),
765 							cellname, io_suelineno, suevarname);
766 						break;
767 					}
768 					xpos = 0;
769 					pos = (ni->highy - ni->lowy) / 2 - varindex * varoffset;
770 					ypos = pos * 4 / lambda;
771 					isparam = TRUE;
772 				}
773 				newtype = 0;
774 				if (keywords[i][1] == 'W' && keywords[i][2] != 0)
775 				{
776 					infstr = initinfstr();
777 					addstringtoinfstr(infstr, &keywords[i][2]);
778 					addstringtoinfstr(infstr, x_(":"));
779 					addstringtoinfstr(infstr, io_sueparseexpression(keywords[i+1]));
780 					newaddr = (INTBIG)returninfstr(infstr);
781 					newtype = VSTRING;
782 				} else
783 				{
784 					pt = keywords[i+1];
785 					len = estrlen(pt) - 1;
786 					if (tolower(pt[len]) == 'u')
787 					{
788 						pt[len] = 0;
789 						if (isanumber(pt))
790 						{
791 							newaddr = scalefromdispunit((float)eatof(pt), DISPUNITMIC) * WHOLE / numericlambda;
792 							newtype = VFRACT;
793 						}
794 						pt[len] = 'u';
795 					}
796 					if (newtype == 0 && isanumber(pt))
797 					{
798 						newtype = VINTEGER;
799 						newaddr = eatoi(pt);
800 						for(cpt = pt; *cpt != 0; cpt++) if (*cpt == '.' || *cpt == 'e' || *cpt == 'E')
801 						{
802 							f = (float)eatof(pt);
803 							j = (INTBIG)(f * WHOLE);
804 							if (j / WHOLE == f)
805 							{
806 								newtype = VFRACT;
807 								newaddr = j;
808 							} else
809 							{
810 								newtype = VFLOAT;
811 								newaddr = castint(f);
812 							}
813 							break;
814 						}
815 					}
816 					if (newtype == 0)
817 					{
818 						newaddr = (INTBIG)io_sueparseexpression(pt);
819 						newtype = VSTRING;
820 					}
821 				}
822 #if LANGJAVA
823 				/* see if the string should be Java code */
824 				if (newtype == VSTRING)
825 				{
826 					for(cpt = (CHAR *)newaddr; *cpt != 0; cpt++)
827 					{
828 						if (*cpt == '@') break;
829 						if (tolower(*cpt) == 'p' && cpt[1] == '(') break;
830 					}
831 					if (*cpt != 0)
832 						newtype = VFLOAT|VJAVA;
833 				}
834 #endif
835 				var = setval((INTBIG)ni, VNODEINST, suevarname, newaddr,
836 					newtype|VDISPLAY);
837 				if (var != NOVARIABLE)
838 				{
839 					defaulttextdescript(var->textdescript, ni->geom);
840 					varindex++;
841 					TDSETOFF(var->textdescript, xpos, ypos);
842 					if (halvesize)
843 						TDSETSIZE(var->textdescript, TDGETSIZE(var->textdescript)/2);
844 					if (isparam)
845 					{
846 						TDSETISPARAM(var->textdescript, VTISPARAMETER);
847 						TDSETDISPPART(var->textdescript, VTDISPLAYNAMEVALUE);
848 
849 						/* make sure the parameter exists in the cell definition */
850 						cnp = contentsview(ni->proto);
851 						if (cnp == NONODEPROTO) cnp = ni->proto;
852 						var = getval((INTBIG)cnp, VNODEPROTO, -1, suevarname);
853 						if (var == NOVARIABLE)
854 						{
855 							var = setval((INTBIG)cnp, VNODEPROTO, suevarname, newaddr,
856 								newtype|VDISPLAY);
857 							if (var != NOVARIABLE)
858 							{
859 								TDSETISPARAM(var->textdescript, VTISPARAMETER);
860 								TDSETDISPPART(var->textdescript, VTDISPLAYNAMEVALINH);
861 							}
862 						}
863 					}
864 				}
865 			}
866 			continue;
867 		}
868 
869 		/* handle "make_wire" for defining arcs */
870 		if (namesame(keywords[0], x_("make_wire")) == 0)
871 		{
872 			sw = io_suenewwire();
873 			sw->x[0] = io_suemakex(eatoi(keywords[1]), lambda);
874 			sw->y[0] = io_suemakey(eatoi(keywords[2]), lambda);
875 			sw->x[1] = io_suemakex(eatoi(keywords[3]), lambda);
876 			sw->y[1] = io_suemakey(eatoi(keywords[4]), lambda);
877 			sw->nextsuewire = firstsuewire;
878 			firstsuewire = sw;
879 			continue;
880 		}
881 
882 		/* handle "icon_term" for defining ports in icons */
883 		if (namesame(keywords[0], x_("icon_term")) == 0)
884 		{
885 			io_sueparseparameters(&keywords[1], count-1, &x, &y, lambda, &rot, &trn,
886 				&type, &thename, &thelabel, &thetext, dia);
887 			estrcpy(suevarname, thename);
888 
889 			proto = sch_buspinprim;
890 			defaultnodesize(proto, &px, &py);
891 			lx = x - px/2;   hx = lx + px;
892 			ly = y - py/2;   hy = ly + py;
893 			ni = newnodeinst(proto, lx, hx, ly, hy, trn, rot%3600, cell);
894 			if (ni == NONODEINST) continue;
895 			endobjectchange((INTBIG)ni, VNODEINST);
896 
897 			ppt = io_suenewexport(cell, ni, proto->firstportproto, suevarname);
898 			if (ppt == NOPORTPROTO)
899 			{
900 				ttyputmsg(_("Cell %s, line %ld, could not create port %s"),
901 					cellname, io_suelineno, suevarname);
902 			} else
903 			{
904 				defaulttextdescript(ppt->textdescript, NOGEOM);
905 				defaulttextsize(1, ppt->textdescript);
906 				ppt->userbits = (ppt->userbits & ~STATEBITS) | type;
907 				endobjectchange((INTBIG)ppt, VPORTPROTO);
908 			}
909 			continue;
910 		}
911 
912 		/* handle "icon_arc" for defining icon curves */
913 		if (namesame(keywords[0], x_("icon_arc")) == 0)
914 		{
915 			if (count != 9)
916 			{
917 				ttyputerr(_("Cell %s, line %ld: needs 9 arguments, has %ld: %s"),
918 					cellname, io_suelineno, count, io_sueorigline);
919 				continue;
920 			}
921 			start = 0;   extent = 359;
922 			p1x = io_suemakex(eatoi(keywords[1]), lambda);
923 			p1y = io_suemakey(eatoi(keywords[2]), lambda);
924 			p2x = io_suemakex(eatoi(keywords[3]), lambda);
925 			p2y = io_suemakey(eatoi(keywords[4]), lambda);
926 			if (namesame(keywords[5], x_("-start")) == 0) start = eatoi(keywords[6]);
927 			if (namesame(keywords[7], x_("-extent")) == 0) extent = eatoi(keywords[8]);
928 			lx = mini(p1x, p2x);   hx = maxi(p1x, p2x);
929 			ly = mini(p1y, p2y);   hy = maxi(p1y, p2y);
930 
931 			ni = newnodeinst(art_circleprim, lx, hx, ly, hy, 0, 0, cell);
932 			if (ni == NONODEINST) continue;
933 			if (extent != 359)
934 			{
935 				if (extent < 0)
936 				{
937 					start += extent;
938 					extent = -extent;
939 				}
940 				rextent = extent+1;   rextent = rextent * EPI / 180.0;
941 				rstart = start * EPI / 180.0;
942 				setarcdegrees(ni, rstart, rextent);
943 			}
944 			endobjectchange((INTBIG)ni, VNODEINST);
945 			continue;
946 		}
947 
948 		/* handle "icon_line" for defining icon outlines */
949 		if (namesame(keywords[0], x_("icon_line")) == 0)
950 		{
951 			for(i=1; i<count; i++)
952 			{
953 				if (namesame(keywords[i], x_("-tags")) == 0) break;
954 				if ((i%2) != 0) outline[i-1] = io_suemakex(eatoi(keywords[i]), lambda); else
955 					outline[i-1] = io_suemakey(eatoi(keywords[i]), lambda);
956 			}
957 			keycount = i / 2;
958 
959 			/* determine bounds of icon */
960 			lx = hx = outline[0];
961 			ly = hy = outline[1];
962 			for(i=1; i<keycount; i++)
963 			{
964 				if (outline[i*2]   < lx) lx = outline[i*2];
965 				if (outline[i*2]   > hx) hx = outline[i*2];
966 				if (outline[i*2+1] < ly) ly = outline[i*2+1];
967 				if (outline[i*2+1] > hy) hy = outline[i*2+1];
968 			}
969 			ni = newnodeinst(art_openedpolygonprim, lx, hx, ly, hy, 0, 0, cell);
970 			if (ni == NONODEINST) return(NONODEPROTO);
971 			cx = (lx + hx) / 2;  cy = (ly + hy) / 2;
972 			for(i=0; i<keycount; i++)
973 			{
974 				outline[i*2] -= cx;   outline[i*2+1] -= cy;
975 			}
976 			(void)setvalkey((INTBIG)ni, VNODEINST, el_trace_key, (INTBIG)outline,
977 				VINTEGER|VISARRAY|((keycount*2)<<VLENGTHSH));
978 			endobjectchange((INTBIG)ni, VNODEINST);
979 			continue;
980 		}
981 
982 		/* handle "icon_setup" for defining variables */
983 		if (namesame(keywords[0], x_("icon_setup")) == 0)
984 		{
985 			/* extract parameters */
986 			if (namesame(keywords[1], x_("$args")) != 0)
987 			{
988 				ttyputerr(_("Cell %s, line %ld: has unrecognized 'icon_setup'"),
989 					cellname, io_suelineno);
990 				continue;
991 			}
992 			pt = keywords[2];
993 			if (*pt == '{') pt++;
994 			for(;;)
995 			{
996 				while (*pt == ' ') pt++;
997 				if (*pt == '}' || *pt == 0) break;
998 
999 				/* collect up to a space or close curly */
1000 				startkey = pt;
1001 				curly = 0;
1002 				for(;;)
1003 				{
1004 					if (curly == 0)
1005 					{
1006 						if (*pt == 0 || *pt == ' ' || *pt == '}') break;
1007 					}
1008 					if (*pt == '{') curly++;
1009 					if (*pt == '}') curly--;
1010 					if (*pt == 0) break;
1011 					pt++;
1012 				}
1013 				save = *pt;
1014 				*pt = 0;
1015 
1016 				/* parse the keyword pair in "startkey" */
1017 				i = numargs+1;
1018 				newargnames = (CHAR **)emalloc(i * (sizeof (CHAR *)), el_tempcluster);
1019 				newargvalues = (CHAR **)emalloc(i * (sizeof (CHAR *)), el_tempcluster);
1020 				for(i=0; i<numargs; i++)
1021 				{
1022 					/* LINTED "argnames" used in proper order */
1023 					newargnames[i] = argnames[i];
1024 
1025 					/* LINTED "argvalues" used in proper order */
1026 					newargvalues[i] = argvalues[i];
1027 				}
1028 				if (numargs > 0)
1029 				{
1030 					efree((CHAR *)argnames);
1031 					efree((CHAR *)argvalues);
1032 				}
1033 				argnames = newargnames;
1034 				argvalues = newargvalues;
1035 				startkey++;
1036 				for(cpt = startkey; *cpt != 0; cpt++) if (*cpt == ' ') break;
1037 				if (*cpt != 0) *cpt++ = 0;
1038 				(void)allocstring(&argnames[numargs], startkey, el_tempcluster);
1039 				while (*cpt == ' ') cpt++;
1040 				if (*cpt == '{') cpt++;
1041 				startkey = cpt;
1042 				for(cpt = startkey; *cpt != 0; cpt++) if (*cpt == '}') break;
1043 				if (*cpt != 0) *cpt++ = 0;
1044 				(void)allocstring(&argvalues[numargs], startkey, el_tempcluster);
1045 				numargs++;
1046 
1047 				*pt = save;
1048 			}
1049 			continue;
1050 		}
1051 
1052 		/* handle "icon_property" for defining icon strings */
1053 		if (namesame(keywords[0], x_("icon_property")) == 0)
1054 		{
1055 			/* extract parameters */
1056 			io_sueparseparameters(&keywords[1], count-1, &x, &y, lambda, &rot, &trn,
1057 				&type, &thename, &thelabel, &thetext, dia);
1058 			if (thelabel == 0) continue;
1059 
1060 			/* substitute parameters */
1061 			infstr = initinfstr();
1062 			for(pt = thelabel; *pt != 0; pt++)
1063 			{
1064 				if (*pt == '$')
1065 				{
1066 					for(i=0; i<numargs; i++)
1067 						if (namesamen(&pt[1], argnames[i], estrlen(argnames[i])) == 0) break;
1068 					if (i < numargs)
1069 					{
1070 						addstringtoinfstr(infstr, argvalues[i]);
1071 						pt += estrlen(argnames[i]);
1072 						continue;
1073 					}
1074 				}
1075 				addtoinfstr(infstr, *pt);
1076 			}
1077 			thelabel = returninfstr(infstr);
1078 
1079 			ni = newnodeinst(gen_invispinprim, x, x, y, y, 0, 0, cell);
1080 			if (ni == NONODEINST) continue;
1081 			var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)thelabel, VSTRING|VDISPLAY);
1082 			if (var != NOVARIABLE)
1083 			{
1084 				defaulttextsize(2, var->textdescript);
1085 				TDSETPOS(var->textdescript, VTPOSCENT);
1086 			}
1087 			endobjectchange((INTBIG)ni, VNODEINST);
1088 			continue;
1089 		}
1090 
1091 		/* handle "make_text" for placing strings */
1092 		if (namesame(keywords[0], x_("make_text")) == 0)
1093 		{
1094 			/* extract parameters */
1095 			io_sueparseparameters(&keywords[1], count-1, &x, &y, lambda, &rot, &trn,
1096 				&type, &thename, &thelabel, &thetext, dia);
1097 			if (thetext == 0) continue;
1098 
1099 			ni = newnodeinst(gen_invispinprim, x, x, y, y, 0, 0, cell);
1100 			if (ni == NONODEINST) continue;
1101 			var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)thetext, VSTRING|VDISPLAY);
1102 			if (var != NOVARIABLE)
1103 			{
1104 				defaulttextsize(2, var->textdescript);
1105 				TDSETPOS(var->textdescript, VTPOSCENT);
1106 			}
1107 			endobjectchange((INTBIG)ni, VNODEINST);
1108 			continue;
1109 		}
1110 
1111 		/* ignore known keywords */
1112 		if (namesame(keywords[0], x_("icon_title")) == 0 ||
1113 			namesame(keywords[0], x_("make_line")) == 0 ||
1114 			namesame(keywords[0], x_("}")) == 0)
1115 		{
1116 			continue;
1117 		}
1118 
1119 		ttyputerr(_("Cell %s, line %ld: unknown keyword (%s): %s"), cellname,
1120 			io_suelineno, keywords[0], io_sueorigline);
1121 	}
1122 
1123 	/* place an icon instance in the schematic if requested */
1124 	if (placeicon && schemcell != NONODEPROTO &&
1125 		iconcell != NONODEPROTO)
1126 	{
1127 		px = iconcell->highx - iconcell->lowx;
1128 		py = iconcell->highy - iconcell->lowy;
1129 		lx = iconx - px/2;   hx = lx + px;
1130 		ly = icony - py/2;   hy = ly + py;
1131 		ni = newnodeinst(iconcell, lx, hx, ly, hy, 0, 0, schemcell);
1132 		if (ni != NONODEINST)
1133 		{
1134 			ni->userbits |= NEXPAND;
1135 			endobjectchange((INTBIG)ni, VNODEINST);
1136 		}
1137 	}
1138 
1139 	for(i=0; i<numargs; i++)
1140 	{
1141 		efree(argnames[i]);
1142 		efree(argvalues[i]);
1143 	}
1144 	if (numargs > 0)
1145 	{
1146 		efree((CHAR *)argnames);
1147 		efree((CHAR *)argvalues);
1148 	}
1149 	if (namestrlen > 0) efree(namestring);
1150 
1151 	/* cleanup the current cell */
1152 	if (cell != NONODEPROTO)
1153 	{
1154 		io_sueplacewires(firstsuewire, firstsuenet, cell, lambda);
1155 		io_suefreewires(firstsuewire);
1156 		io_sueplacenets(firstsuenet, cell);
1157 		io_suefreenets(firstsuenet);
1158 	}
1159 
1160 	/* make sure cells are the right size */
1161 	if (schemcell != NONODEPROTO) (*el_curconstraint->solve)(schemcell);
1162 	if (iconcell != NONODEPROTO) (*el_curconstraint->solve)(iconcell);
1163 
1164 	/* return the cell */
1165 	if (schemcell != NONODEPROTO) return(schemcell);
1166 	return(iconcell);
1167 
1168 }
1169 
1170 /*
1171  * Routine to examine a SUE expression and add "@" in front of variable names.
1172  */
io_sueparseexpression(CHAR * expression)1173 CHAR *io_sueparseexpression(CHAR *expression)
1174 {
1175 	REGISTER void *infstr;
1176 	REGISTER CHAR *keyword;
1177 
1178 	infstr = initinfstr();
1179 	while (*expression != 0)
1180 	{
1181 		keyword = getkeyword(&expression, x_(" \t,+-*/()"));
1182 		if (keyword == NOSTRING) break;
1183 		if (*keyword != 0)
1184 		{
1185 			if (isdigit(keyword[0]))
1186 			{
1187 				addstringtoinfstr(infstr, keyword);
1188 			} else
1189 			{
1190 				if (*expression != '(')
1191 					addtoinfstr(infstr, '@');
1192 				addstringtoinfstr(infstr, keyword);
1193 			}
1194 			if (*expression != 0)
1195 				addtoinfstr(infstr, *expression++);
1196 		}
1197 	}
1198 	return(returninfstr(infstr));
1199 }
1200 
1201 /*
1202  * Routine to parse the "count" parameters in "keywords" and fill in the values
1203  * that are found.  Fills in:
1204  * "-origin"  placed into "x" and "y"
1205  * "-orient"  placed into "rot" and "trn"
1206  * "-type"    placed into "type"
1207  * "-name"    placed into "thename".
1208  * "-label"   placed into "thelabel".
1209  * "-text"    placed into "thetext".
1210  */
io_sueparseparameters(CHAR ** keywords,INTBIG count,INTBIG * x,INTBIG * y,INTBIG lambda,INTBIG * rot,INTBIG * trn,INTBIG * type,CHAR ** thename,CHAR ** thelabel,CHAR ** thetext,void * dia)1211 void io_sueparseparameters(CHAR **keywords, INTBIG count, INTBIG *x, INTBIG *y, INTBIG lambda,
1212 	INTBIG *rot, INTBIG *trn, INTBIG *type, CHAR **thename, CHAR **thelabel, CHAR **thetext,
1213 	void *dia)
1214 {
1215 	CHAR *pt;
1216 	REGISTER INTBIG textloc, i;
1217 	REGISTER void *infstr;
1218 
1219 	*x = *y = 0;
1220 	*rot = 0;
1221 	*trn = 0;
1222 	*type = 0;
1223 	*thename = 0;
1224 	*thelabel = 0;
1225 	*thetext = 0;
1226 	for(i=0; i<count; i += 2)
1227 	{
1228 		if (namesame(keywords[i], x_("-origin")) == 0)
1229 		{
1230 			pt = keywords[i+1];
1231 			if (*pt == '{') pt++;
1232 			*x = eatoi(pt);
1233 			while (*pt != ' ' && *pt != '\t' && *pt != 0) pt++;
1234 			while (*pt == ' ' || *pt == '\t') pt++;
1235 			*y = eatoi(pt);
1236 		}
1237 		if (namesame(keywords[i], x_("-orient")) == 0)
1238 		{
1239 			if (namesame(keywords[i+1], x_("R90"))  == 0) { *rot = 900;  } else
1240 			if (namesame(keywords[i+1], x_("R270")) == 0) { *rot = 2700; } else
1241 			if (namesame(keywords[i+1], x_("RXY"))  == 0) { *rot = 1800; } else
1242 			if (namesame(keywords[i+1], x_("RY"))   == 0) { *rot = 900;  *trn = 1; } else
1243 			if (namesame(keywords[i+1], x_("R90X")) == 0) { *rot = 0;    *trn = 1; } else
1244 			if (namesame(keywords[i+1], x_("R90Y")) == 0) { *rot = 1800; *trn = 1; } else
1245 			if (namesame(keywords[i+1], x_("RX"))   == 0) { *rot = 2700; *trn = 1; }
1246 		}
1247 		if (namesame(keywords[i], x_("-type")) == 0)
1248 		{
1249 			if (namesame(keywords[i+1], x_("input")) == 0) *type = INPORT; else
1250 			if (namesame(keywords[i+1], x_("output")) == 0) *type = OUTPORT; else
1251 			if (namesame(keywords[i+1], x_("inout")) == 0) *type = BIDIRPORT;
1252 		}
1253 		if (namesame(keywords[i], x_("-name")) == 0 ||
1254 			namesame(keywords[i], x_("-label")) == 0 ||
1255 			namesame(keywords[i], x_("-text")) == 0)
1256 		{
1257 			if (namesame(keywords[i], x_("-name")) == 0) textloc = 0; else
1258 				if (namesame(keywords[i], x_("-label")) == 0) textloc = 1; else
1259 					textloc = 2;
1260 			infstr = initinfstr();
1261 			pt = keywords[i+1];
1262 			if (pt[0] != '{') addstringtoinfstr(infstr, pt); else
1263 			{
1264 				pt++;
1265 				for(;;)
1266 				{
1267 					while (*pt != 0)
1268 					{
1269 						if (*pt == '}') break;
1270 						addtoinfstr(infstr, *pt);
1271 						pt++;
1272 					}
1273 					if (*pt == '}') break;
1274 					addtoinfstr(infstr, ' ');
1275 
1276 					count = io_suegetnextline(keywords, 1, dia);
1277 					if (count < 0) break;
1278 					if (count == 0) continue;
1279 					pt = keywords[0];
1280 					i = -1;
1281 				}
1282 			}
1283 			switch (textloc)
1284 			{
1285 				case 0: *thename = returninfstr(infstr);    break;
1286 				case 1: *thelabel = returninfstr(infstr);   break;
1287 				case 2: *thetext = returninfstr(infstr);    break;
1288 			}
1289 		}
1290 	}
1291 	*x = io_suemakex(*x, lambda);
1292 	*y = io_suemakey(*y, lambda);
1293 	*rot = (3600 - *rot) % 3600;
1294 }
1295 
1296 /*
1297  * Routine to find cell "protoname" in library "lib".
1298  */
io_suegetnodeproto(LIBRARY * lib,CHAR * protoname)1299 NODEPROTO *io_suegetnodeproto(LIBRARY *lib, CHAR *protoname)
1300 {
1301 	REGISTER NODEPROTO *np;
1302 
1303 	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1304 		if (namesame(protoname, np->protoname) == 0) return(np);
1305 	return(NONODEPROTO);
1306 }
1307 
1308 /*
1309  * Routine to create a port called "thename" on port "pp" of node "ni" in cell "cell".
1310  * The name is modified if it already exists.
1311  */
io_suenewexport(NODEPROTO * cell,NODEINST * ni,PORTPROTO * pp,CHAR * thename)1312 PORTPROTO *io_suenewexport(NODEPROTO *cell, NODEINST *ni, PORTPROTO *pp, CHAR *thename)
1313 {
1314 	REGISTER PORTPROTO *ppt;
1315 	REGISTER INTBIG i;
1316 	REGISTER CHAR *portname, *pt;
1317 	CHAR numbuf[20];
1318 	REGISTER void *infstr;
1319 
1320 	portname = thename;
1321 	for(i=0; ; i++)
1322 	{
1323 		ppt = getportproto(cell, portname);
1324 		if (ppt == NOPORTPROTO)
1325 		{
1326 			ppt = newportproto(cell, ni, pp, portname);
1327 			break;
1328 		}
1329 
1330 		/* make space for modified name */
1331 		infstr = initinfstr();
1332 		for(pt = thename; *pt != 0 && *pt != '['; pt++)
1333 			addtoinfstr(infstr, *pt);
1334 		esnprintf(numbuf, 20, x_("-%ld"), i);
1335 		addstringtoinfstr(infstr, numbuf);
1336 		for( ; *pt != 0; pt++)
1337 			addtoinfstr(infstr, *pt);
1338 		portname = returninfstr(infstr);
1339 	}
1340 	return(ppt);
1341 }
1342 
1343 /* Routine to convert SUE X coordinate "x" to Electric coordinates */
io_suemakex(INTBIG x,INTBIG lambda)1344 INTBIG io_suemakex(INTBIG x, INTBIG lambda)
1345 {
1346 	return(x * lambda / 8);
1347 }
1348 
1349 /* Routine to convert SUE Y coordinate "y" to Electric coordinates */
io_suemakey(INTBIG y,INTBIG lambda)1350 INTBIG io_suemakey(INTBIG y, INTBIG lambda)
1351 {
1352 	return(-y * lambda / 8);
1353 }
1354 
1355 /*
1356  * Routine to place all SUE nets into the cell (they are in a linked
1357  * list headed by "firstsuenet").
1358  */
io_sueplacenets(SUENET * firstsuenet,NODEPROTO * cell)1359 void io_sueplacenets(SUENET *firstsuenet, NODEPROTO *cell)
1360 {
1361 	SUENET *sn;
1362 	REGISTER INTBIG pass;
1363 	REGISTER BOOLEAN isbus;
1364 	REGISTER ARCINST *ai, *bestai;
1365 	REGISTER INTBIG cx, cy, dist, bestdist, sea;
1366 	REGISTER GEOM *geom;
1367 	REGISTER CHAR *netname, *busname;
1368 	REGISTER void *infstr;
1369 
1370 	/* 3 passes: qualified labels, unqualified busses, unqualified wires */
1371 	for(pass=0; pass<3; pass++)
1372 	{
1373 		for(sn = firstsuenet; sn != NOSUENET; sn = sn->nextsuenet)
1374 		{
1375 			/* unqualified labels (starting with "[") happen second */
1376 			if (*sn->label == '[')
1377 			{
1378 				/* unqualified label: pass 2 or 3 only */
1379 				if (pass == 0) continue;
1380 			} else
1381 			{
1382 				/* qualified label: pass 1 only */
1383 				if (pass != 0) continue;
1384 			}
1385 
1386 			/* see if this is a bus */
1387 			if (net_buswidth(sn->label) > 1) isbus = TRUE; else isbus = FALSE;
1388 
1389 			sea = initsearch(sn->x, sn->x, sn->y, sn->y, cell);
1390 			bestai = NOARCINST;
1391 			for(;;)
1392 			{
1393 				geom = nextobject(sea);
1394 				if (geom == NOGEOM) break;
1395 				if (geom->entryisnode) continue;
1396 				ai = geom->entryaddr.ai;
1397 				if (isbus)
1398 				{
1399 					if (ai->proto != sch_busarc) continue;
1400 				} else
1401 				{
1402 					if (ai->proto == sch_busarc) continue;
1403 				}
1404 				cx = (ai->end[0].xpos + ai->end[1].xpos) / 2;
1405 				cy = (ai->end[0].ypos + ai->end[1].ypos) / 2;
1406 				dist = computedistance(cx, cy, sn->x, sn->y);
1407 
1408 				/* LINTED "bestdist" used in proper order */
1409 				if (bestai == NOARCINST || dist < bestdist)
1410 				{
1411 					bestai = ai;
1412 					bestdist = dist;
1413 				}
1414 			}
1415 			if (bestai != NOARCINST)
1416 			{
1417 				if (pass == 1)
1418 				{
1419 					/* only allow busses */
1420 					if (bestai->proto != sch_busarc) continue;
1421 				} else if (pass == 2)
1422 				{
1423 					/* disallow busses */
1424 					if (bestai->proto == sch_busarc) continue;
1425 				}
1426 				netname = sn->label;
1427 				if (*netname == '[')
1428 				{
1429 					/* find the proper name of the network */
1430 					busname = io_suefindbusname(bestai);
1431 					if (busname != 0)
1432 					{
1433 						infstr = initinfstr();
1434 						addstringtoinfstr(infstr, busname);
1435 						addstringtoinfstr(infstr, netname);
1436 						netname = returninfstr(infstr);
1437 					}
1438 				}
1439 				us_setarcname(bestai, netname);
1440 			}
1441 		}
1442 	}
1443 }
1444 
1445 /*
1446  * Routine to start at "ai" and search all wires until it finds a named bus.
1447  * Returns zero if no bus name is found.
1448  */
io_suefindbusname(ARCINST * ai)1449 CHAR *io_suefindbusname(ARCINST *ai)
1450 {
1451 	REGISTER ARCINST *oai;
1452 	REGISTER CHAR *busname, *pt;
1453 	REGISTER VARIABLE *var;
1454 	static CHAR pseudobusname[50];
1455 	REGISTER INTBIG index, len;
1456 
1457 	for(oai = ai->parent->firstarcinst; oai != NOARCINST; oai = oai->nextarcinst)
1458 		oai->temp1 = 0;
1459 	busname = io_suesearchbusname(ai);
1460 	if (busname == 0)
1461 	{
1462 		for(index=1; ; index++)
1463 		{
1464 			esnprintf(pseudobusname, 50, x_("NET%ld"), index);
1465 			len = estrlen(pseudobusname);
1466 			for(oai = ai->parent->firstarcinst; oai != NOARCINST; oai = oai->nextarcinst)
1467 			{
1468 				var = getvalkey((INTBIG)oai, VARCINST, VSTRING, el_arc_name_key);
1469 				if (var == NOVARIABLE) continue;
1470 				pt = (CHAR *)var->addr;
1471 				if (namesame(pseudobusname, pt) == 0) break;
1472 				if (namesamen(pseudobusname, pt, len) == 0 &&
1473 					pt[len] == '[') break;
1474 			}
1475 			if (oai == NOARCINST) break;
1476 		}
1477 		busname = pseudobusname;
1478 	}
1479 	return(busname);
1480 }
1481 
io_suesearchbusname(ARCINST * ai)1482 CHAR *io_suesearchbusname(ARCINST *ai)
1483 {
1484 	REGISTER ARCINST *oai;
1485 	REGISTER CHAR *busname;
1486 	REGISTER INTBIG i;
1487 	REGISTER NODEINST *ni;
1488 	REGISTER PORTARCINST *pi;
1489 	REGISTER PORTEXPINST *pe;
1490 	REGISTER PORTPROTO *pp;
1491 	REGISTER VARIABLE *var;
1492 	REGISTER void *infstr;
1493 
1494 	ai->temp1 = 1;
1495 	if (ai->proto == sch_busarc)
1496 	{
1497 		var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
1498 		if (var != NOVARIABLE)
1499 		{
1500 			infstr = initinfstr();
1501 			for(busname = (CHAR *)var->addr; *busname != 0; busname++)
1502 			{
1503 				if (*busname == '[') break;
1504 				addtoinfstr(infstr, *busname);
1505 			}
1506 			return(returninfstr(infstr));
1507 		}
1508 	}
1509 	for(i=0; i<2; i++)
1510 	{
1511 		ni = ai->end[i].nodeinst;
1512 		if (ni->proto != sch_wirepinprim && ni->proto != sch_buspinprim &&
1513 			ni->proto != sch_offpageprim) continue;
1514 		if (ni->proto == sch_buspinprim || ni->proto == sch_offpageprim)
1515 		{
1516 			/* see if there is an arrayed port here */
1517 			for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
1518 			{
1519 				pp = pe->exportproto;
1520 				for(busname = pp->protoname; *busname != 0; busname++)
1521 					if (*busname == '[') break;
1522 				if (*busname != 0)
1523 				{
1524 					infstr = initinfstr();
1525 					for(busname = pp->protoname; *busname != 0; busname++)
1526 					{
1527 						if (*busname == '[') break;
1528 						addtoinfstr(infstr, *busname);
1529 					}
1530 					return(returninfstr(infstr));
1531 				}
1532 			}
1533 		}
1534 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1535 		{
1536 			oai = pi->conarcinst;
1537 			if (oai->temp1 != 0) continue;
1538 			busname = io_suesearchbusname(oai);
1539 			if (busname != 0) return(busname);
1540 		}
1541 	}
1542 	return(0);
1543 }
1544 
1545 /*
1546  * Routine to place all SUE wires into the cell (they are in a linked
1547  * list headed by "firstsuewire").
1548  */
io_sueplacewires(SUEWIRE * firstsuewire,SUENET * firstsuenet,NODEPROTO * cell,INTBIG lambda)1549 void io_sueplacewires(SUEWIRE *firstsuewire, SUENET *firstsuenet, NODEPROTO *cell, INTBIG lambda)
1550 {
1551 	SUEWIRE *sw, *osw;
1552 	SUENET *sn;
1553 	REGISTER INTBIG i, j, wid, px, py, lx, hx, ly, hy, sea, bits;
1554 	REGISTER BOOLEAN propagatedbus, isbus;
1555 	INTBIG xsize, ysize, x, y, ox, oy;
1556 	REGISTER NODEPROTO *proto;
1557 	REGISTER GEOM *geom;
1558 	REGISTER PORTEXPINST *pe;
1559 	NODEINST *ni;
1560 	REGISTER ARCPROTO *ap;
1561 	REGISTER NODEINST *bottomni, *oni;
1562 	PORTPROTO *pp;
1563 	REGISTER PORTPROTO *bottompp, *opp;
1564 	REGISTER ARCINST *ai;
1565 
1566 	/* mark all wire ends as "unassigned", all wire types as unknown */
1567 	for(sw = firstsuewire; sw != NOSUEWIRE; sw = sw->nextsuewire)
1568 	{
1569 		sw->ni[0] = sw->ni[1] = NONODEINST;
1570 		sw->proto = NOARCPROTO;
1571 	}
1572 
1573 	/* examine all network names and assign wire types appropriately */
1574 	for(sn = firstsuenet; sn != NOSUENET; sn = sn->nextsuenet)
1575 	{
1576 		for(sw = firstsuewire; sw != NOSUEWIRE; sw = sw->nextsuewire)
1577 		{
1578 			for(i=0; i<2; i++)
1579 			{
1580 				if (sw->x[i] == sn->x && sw->y[i] == sn->y)
1581 				{
1582 					if (net_buswidth(sn->label) > 1) sw->proto = sch_busarc; else
1583 						sw->proto = sch_wirearc;
1584 				}
1585 			}
1586 		}
1587 	}
1588 
1589 	/* find connections that are exactly on existing nodes */
1590 	for(sw = firstsuewire; sw != NOSUEWIRE; sw = sw->nextsuewire)
1591 	{
1592 		for(i=0; i<2; i++)
1593 		{
1594 			if (sw->ni[i] != NONODEINST) continue;
1595 			for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1596 			{
1597 				pp = io_suewiredport(ni, &sw->x[i], &sw->y[i], sw->x[1-i], sw->y[1-i]);
1598 				if (pp == NOPORTPROTO) continue;
1599 				sw->ni[i] = ni;
1600 				sw->pp[i] = pp;
1601 
1602 				/* determine whether this port is a bus */
1603 				isbus = FALSE;
1604 				bottomni = ni;   bottompp = pp;
1605 				while (bottomni->proto->primindex == 0)
1606 				{
1607 					bottomni = bottompp->subnodeinst;
1608 					bottompp = bottompp->subportproto;
1609 				}
1610 				if (bottomni->proto == sch_wireconprim) continue;
1611 				if (!isbus && ni->proto == sch_offpageprim)
1612 				{
1613 					/* see if there is a bus port on this primitive */
1614 					for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
1615 					{
1616 						if (net_buswidth(pe->exportproto->protoname) > 1) isbus = TRUE;
1617 					}
1618 				}
1619 
1620 				if (isbus)
1621 				{
1622 					sw->proto = sch_busarc;
1623 				} else
1624 				{
1625 					if (sw->proto == NOARCPROTO)
1626 						sw->proto = sch_wirearc;
1627 				}
1628 			}
1629 		}
1630 	}
1631 
1632 	/* now iteratively extend bus wires to connections with others */
1633 	propagatedbus = TRUE;
1634 	while (propagatedbus)
1635 	{
1636 		propagatedbus = FALSE;
1637 		for(sw = firstsuewire; sw != NOSUEWIRE; sw = sw->nextsuewire)
1638 		{
1639 			if (sw->proto != sch_busarc) continue;
1640 			for(osw = firstsuewire; osw != NOSUEWIRE; osw = osw->nextsuewire)
1641 			{
1642 				if (osw->proto != NOARCPROTO) continue;
1643 				for(i=0; i<2; i++)
1644 				{
1645 					for(j=0; j<2; j++)
1646 					{
1647 						if (sw->x[i] == osw->x[j] && sw->y[i] == osw->y[j])
1648 						{
1649 							/* common point found: continue the bus request */
1650 							osw->proto = sch_busarc;
1651 							propagatedbus = TRUE;
1652 						}
1653 					}
1654 				}
1655 			}
1656 		}
1657 	}
1658 
1659 	/* now make pins where wires meet */
1660 	for(sw = firstsuewire; sw != NOSUEWIRE; sw = sw->nextsuewire)
1661 	{
1662 		for(i=0; i<2; i++)
1663 		{
1664 			if (sw->ni[i] != NONODEINST) continue;
1665 			if (sw->proto == sch_busarc) proto = sch_buspinprim; else
1666 				proto = sch_wirepinprim;
1667 
1668 			/* look at all other wires at this point and figure out type of pin to make */
1669 			for(osw = firstsuewire; osw != NOSUEWIRE; osw = osw->nextsuewire)
1670 			{
1671 				if (osw == sw) continue;
1672 				for(j=0; j<2; j++)
1673 				{
1674 					if (sw->x[i] != osw->x[j] || sw->y[i] != osw->y[j]) continue;
1675 					if (osw->ni[j] != NONODEINST)
1676 					{
1677 						sw->ni[i] = osw->ni[j];
1678 						sw->pp[i] = osw->pp[j];
1679 						break;
1680 					}
1681 					if (osw->proto == sch_busarc) proto = sch_buspinprim;
1682 				}
1683 				if (sw->ni[i] != NONODEINST) break;
1684 			}
1685 
1686 			/* make the pin if it doesn't exist */
1687 			if (sw->ni[i] == NONODEINST)
1688 			{
1689 				/* common point found: make a pin */
1690 				defaultnodesize(proto, &xsize , &ysize);
1691 				sw->ni[i] = newnodeinst(proto, sw->x[i] - xsize/2,
1692 					sw->x[i] + xsize/2, sw->y[i] - ysize/2,
1693 					sw->y[i] + ysize/2, 0, 0, cell);
1694 				endobjectchange((INTBIG)sw->ni[i], VNODEINST);
1695 				sw->pp[i] = proto->firstportproto;
1696 			}
1697 
1698 			/* put that node in all appropriate locations */
1699 			for(osw = firstsuewire; osw != NOSUEWIRE; osw = osw->nextsuewire)
1700 			{
1701 				if (osw == sw) continue;
1702 				for(j=0; j<2; j++)
1703 				{
1704 					if (sw->x[i] != osw->x[j] || sw->y[i] != osw->y[j]) continue;
1705 					if (osw->ni[j] != NONODEINST) continue;
1706 					osw->ni[j] = sw->ni[i];
1707 					osw->pp[j] = sw->pp[i];
1708 				}
1709 			}
1710 		}
1711 	}
1712 
1713 	/* make pins at all of the remaining wire ends */
1714 	for(sw = firstsuewire; sw != NOSUEWIRE; sw = sw->nextsuewire)
1715 	{
1716 		for(i=0; i<2; i++)
1717 		{
1718 			if (sw->ni[i] != NONODEINST) continue;
1719 			if (!io_suefindnode(&sw->x[i], &sw->y[i], sw->x[1-i], sw->y[1-i], cell,
1720 				&sw->ni[i], &sw->pp[i], sw->ni[1-i], lambda))
1721 			{
1722 				if (sw->proto == sch_busarc) proto = sch_buspinprim; else
1723 					proto = sch_wirepinprim;
1724 				defaultnodesize(proto, &xsize , &ysize);
1725 				sw->ni[i] = newnodeinst(proto, sw->x[i] - xsize/2,
1726 					sw->x[i] + xsize/2, sw->y[i] - ysize/2, sw->y[i] + ysize/2,
1727 					0, 0, cell);
1728 				endobjectchange((INTBIG)sw->ni[i], VNODEINST);
1729 				sw->pp[i] = sw->ni[i]->proto->firstportproto;
1730 			}
1731 		}
1732 	}
1733 
1734 	/* now make the connections */
1735 	for(sw = firstsuewire; sw != NOSUEWIRE; sw = sw->nextsuewire)
1736 	{
1737 		if (sw->proto == NOARCPROTO) sw->proto = sch_wirearc;
1738 		wid = defaultarcwidth(sw->proto);
1739 
1740 		/* if this is a bus, make sure it can connect */
1741 		if (sw->proto == sch_busarc)
1742 		{
1743 			for(i=0; i<2; i++)
1744 			{
1745 				for(j=0; sw->pp[i]->connects[j] != NOARCPROTO; j++)
1746 					if (sw->pp[i]->connects[j] == sch_busarc) break;
1747 				if (sw->pp[i]->connects[j] == NOARCPROTO)
1748 				{
1749 					/* this end cannot connect: fake the connection */
1750 					px = (sw->x[0] + sw->x[1]) / 2;
1751 					py = (sw->y[0] + sw->y[1]) / 2;
1752 					defaultnodesize(sch_buspinprim, &xsize , &ysize);
1753 					lx = px - xsize/2;   hx = lx + xsize;
1754 					ly = py - ysize/2;   hy = ly + ysize;
1755 					ni = newnodeinst(sch_buspinprim, lx, hx, ly, hy, 0, 0, cell);
1756 					if (ni == NONODEINST) break;
1757 					endobjectchange((INTBIG)ni, VNODEINST);
1758 					pp = ni->proto->firstportproto;
1759 					ai = newarcinst(gen_unroutedarc, defaultarcwidth(gen_unroutedarc),
1760 						us_makearcuserbits(gen_unroutedarc), ni, pp, px, py,
1761 							sw->ni[i], sw->pp[i], sw->x[i], sw->y[i], cell);
1762 					if (ai == NOARCINST)
1763 					{
1764 						ttyputerr(_("Error making fake connection"));
1765 						break;
1766 					}
1767 					endobjectchange((INTBIG)ai, VARCINST);
1768 					sw->ni[i] = ni;
1769 					sw->pp[i] = pp;
1770 					sw->x[i] = px;
1771 					sw->y[i] = py;
1772 				}
1773 			}
1774 		}
1775 
1776 		ai = newarcinst(sw->proto, wid, us_makearcuserbits(sw->proto),
1777 			sw->ni[0], sw->pp[0], sw->x[0], sw->y[0],
1778 			sw->ni[1], sw->pp[1], sw->x[1], sw->y[1], cell);
1779 		if (ai == NOARCINST)
1780 		{
1781 			ttyputerr(_("Could not run a wire from %s to %s in cell %s"),
1782 				describenodeinst(sw->ni[0]), describenodeinst(sw->ni[1]),
1783 					describenodeproto(cell));
1784 			continue;
1785 		}
1786 
1787 		/* negate the wire if requested */
1788 		if (sw->ni[0]->temp1 != 0 &&
1789 			estrcmp(sw->pp[0]->protoname, x_("y")) == 0)
1790 		{
1791 			ai->userbits |= ISNEGATED;
1792 		} else if (sw->ni[1]->temp1 != 0 &&
1793 			estrcmp(sw->pp[1]->protoname, x_("y")) == 0)
1794 		{
1795 			ai->userbits |= ISNEGATED | REVERSEEND;
1796 		}
1797 		endobjectchange((INTBIG)ai, VARCINST);
1798 	}
1799 
1800 	/* now look for implicit connections where "offpage" connectors touch */
1801 	for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1802 	{
1803 		if (ni->proto != sch_offpageprim) continue;
1804 		if (ni->firstportarcinst != NOPORTARCINST) continue;
1805 		pp = ni->proto->firstportproto->nextportproto;
1806 		portposition(ni, pp, &x, &y);
1807 		sea = initsearch(x, x, y, y, cell);
1808 		for(;;)
1809 		{
1810 			geom = nextobject(sea);
1811 			if (geom == NOGEOM) break;
1812 			if (!geom->entryisnode) continue;
1813 			oni = geom->entryaddr.ni;
1814 			if (oni == ni) continue;
1815 			for(opp = oni->proto->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
1816 			{
1817 				portposition(oni, opp, &ox, &oy);
1818 				if (ox != x || oy != y) continue;
1819 				for(i=0; i<3; i++)
1820 				{
1821 					switch (i)
1822 					{
1823 						case 0: ap = sch_busarc;      break;
1824 						case 1: ap = sch_wirearc;     break;
1825 						case 2: ap = gen_unroutedarc; break;
1826 					}
1827 					for(j=0; pp->connects[j] != NOARCPROTO; j++)
1828 						if (pp->connects[j] == ap) break;
1829 					if (pp->connects[j] == NOARCPROTO) continue;
1830 					for(j=0; opp->connects[j] != NOARCPROTO; j++)
1831 						if (opp->connects[j] == ap) break;
1832 					if (opp->connects[j] == NOARCPROTO) continue;
1833 					break;
1834 				}
1835 
1836 				wid = defaultarcwidth(ap);
1837 				bits = us_makearcuserbits(ap);
1838 				ai = newarcinst(ap, wid, bits, ni, pp, x, y, oni, opp, x, y, cell);
1839 				if (ai != NOARCINST)
1840 					endobjectchange((INTBIG)ai, VARCINST);
1841 				break;
1842 			}
1843 			if (opp != NOPORTPROTO) break;
1844 		}
1845 	}
1846 }
1847 
1848 /*
1849  * Routine to find the port on node "ni" that attaches to the wire from (x,y) to (ox,oy).
1850  * Returns NOPORTPROTO if not found.
1851  */
io_suewiredport(NODEINST * ni,INTBIG * x,INTBIG * y,INTBIG ox,INTBIG oy)1852 PORTPROTO *io_suewiredport(NODEINST *ni, INTBIG *x, INTBIG *y, INTBIG ox, INTBIG oy)
1853 {
1854 	REGISTER PORTPROTO *pp, *bestpp;
1855 	REGISTER INTBIG dist, bestdist;
1856 	INTBIG px, py;
1857 	static POLYGON *poly = NOPOLYGON;
1858 
1859 	/* make sure there is a polygon */
1860 	(void)needstaticpolygon(&poly, 4, io_tool->cluster);
1861 	for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1862 	{
1863 		shapeportpoly(ni, pp, poly, FALSE);
1864 		if (isinside(*x, *y, poly)) return(pp);
1865 	}
1866 	if ((ni->lowx+ni->highx) / 2 != *x ||
1867 		(ni->lowy+ni->highy) / 2 != *y) return(NOPORTPROTO);
1868 
1869 	/* find port that is closest to OTHER end */
1870 	bestdist = MAXINTBIG;
1871 	bestpp = NOPORTPROTO;
1872 	for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1873 	{
1874 		portposition(ni, pp, &px, &py);
1875 		dist = computedistance(px, py, ox, oy);
1876 		if (dist > bestdist) continue;
1877 		bestdist = dist;
1878 		bestpp = pp;
1879 	}
1880 	portposition(ni, bestpp, x, y);
1881 	return(bestpp);
1882 }
1883 
1884 /*
1885  * Routine to find the pin at (x, y) and return it.
1886  */
io_suefindpinnode(INTBIG x,INTBIG y,NODEPROTO * np,PORTPROTO ** thepp)1887 NODEINST *io_suefindpinnode(INTBIG x, INTBIG y, NODEPROTO *np, PORTPROTO **thepp)
1888 {
1889 	REGISTER GEOM *geom;
1890 	REGISTER INTBIG sea;
1891 	REGISTER NODEINST *ni;
1892 	REGISTER PORTPROTO *pp;
1893 	INTBIG px, py;
1894 
1895 	*thepp = NOPORTPROTO;
1896 	sea = initsearch(x, x, y, y, np);
1897 	for(;;)
1898 	{
1899 		geom = nextobject(sea);
1900 		if (geom == NOGEOM) break;
1901 		if (!geom->entryisnode) continue;
1902 		ni = geom->entryaddr.ni;
1903 
1904 		/* find closest port */
1905 		for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1906 		{
1907 			/* make sure there is a polygon */
1908 			portposition(ni, pp, &px, &py);
1909 			if (px == x && py == y)
1910 			{
1911 				*thepp = pp;
1912 				termsearch(sea);
1913 				return(ni);
1914 			}
1915 		}
1916 	}
1917 	return(NONODEINST);
1918 }
1919 
1920 /*
1921  * Routine to find the node at (x, y) and return it.
1922  */
io_suefindnode(INTBIG * x,INTBIG * y,INTBIG ox,INTBIG oy,NODEPROTO * np,NODEINST ** rni,PORTPROTO ** rpp,NODEINST * notthisnode,INTBIG lambda)1923 BOOLEAN io_suefindnode(INTBIG *x, INTBIG *y, INTBIG ox, INTBIG oy,
1924 	NODEPROTO *np, NODEINST **rni, PORTPROTO **rpp, NODEINST *notthisnode, INTBIG lambda)
1925 {
1926 	REGISTER GEOM *geom;
1927 	REGISTER INTBIG sea;
1928 	REGISTER NODEINST *ni, *bestni;
1929 	INTBIG plx, phx, ply, phy;
1930 	REGISTER INTBIG dist, bestdist, thisx, thisy, bestx, besty, slop;
1931 	REGISTER PORTPROTO *pp, *bestpp;
1932 	static POLYGON *poly = NOPOLYGON;
1933 
1934 	slop = lambda * 10;
1935 	sea = initsearch(*x-slop, *x+slop, *y-slop, *y+slop, np);
1936 	bestpp = NOPORTPROTO;
1937 	for(;;)
1938 	{
1939 		geom = nextobject(sea);
1940 		if (geom == NOGEOM) break;
1941 		if (!geom->entryisnode) continue;
1942 		ni = geom->entryaddr.ni;
1943 		if (ni == notthisnode) continue;
1944 
1945 		/* ignore pins */
1946 		if (ni->proto == sch_wirepinprim) continue;
1947 
1948 		/* find closest port */
1949 		for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1950 		{
1951 			/* make sure there is a polygon */
1952 			(void)needstaticpolygon(&poly, 4, io_tool->cluster);
1953 
1954 			/* get the polygon describing the port */
1955 			shapeportpoly(ni, pp, poly, FALSE);
1956 			getbbox(poly, &plx, &phx, &ply, &phy);
1957 
1958 			/* find out if the line crosses the polygon */
1959 			if (*x == ox)
1960 			{
1961 				/* line is vertical: look for intersection with polygon */
1962 				if (ox < plx || ox > phx) continue;
1963 				thisx = ox;
1964 				thisy = (ply+phy)/2;
1965 			} else if (*y == oy)
1966 			{
1967 				/* line is horizontal: look for intersection with polygon */
1968 				if (oy < ply || oy > phy) continue;
1969 				thisx = (plx+phx)/2;
1970 				thisy = oy;
1971 			} else
1972 			{
1973 				if (!isinside(ox, oy, poly)) continue;
1974 				thisx = ox;
1975 				thisy = oy;
1976 			}
1977 
1978 			dist = computedistance(ox, oy, thisx, thisy);
1979 
1980 			/* LINTED "bestdist" used in proper order */
1981 			if (bestpp == NOPORTPROTO || dist < bestdist)
1982 			{
1983 				bestpp = pp;
1984 				bestni = ni;
1985 				bestdist = dist;
1986 				bestx = thisx;
1987 				besty = thisy;
1988 			}
1989 		}
1990 	}
1991 
1992 	/* report the hit */
1993 	if (bestpp == NOPORTPROTO) return(FALSE);
1994 	*rni = bestni;   *rpp = bestpp;
1995 	*x = bestx;      *y = besty;
1996 	return(TRUE);
1997 }
1998 
1999 /*
2000  * Routine to find the SUE file "name" on disk, and read it into library "lib".
2001  * Returns NONODEPROTO if the file is not found or not read properly.
2002  */
io_suereadfromdisk(LIBRARY * lib,CHAR * name,void * dia)2003 NODEPROTO *io_suereadfromdisk(LIBRARY *lib, CHAR *name, void *dia)
2004 {
2005 	REGISTER INTBIG i, savelineno, filepos, savefilesize;
2006 	CHAR suevarname[200], subfilename[300], savesuelastline[MAXLINE], saveorigline[MAXLINE],
2007 		lastprogressmsg[MAXLINE], savecurline[MAXLINE], *truename;
2008 	REGISTER FILE *f, *savefp;
2009 	REGISTER NODEPROTO *proto;
2010 
2011 	/* look for another "sue" file that describes this cell */
2012 	for(i=0; i<io_suenumdirectories; i++)
2013 	{
2014 		estrcpy(subfilename, io_suedirectories[i]);
2015 		estrcat(subfilename, name);
2016 		estrcat(subfilename, x_(".sue"));
2017 		f = xopen(subfilename, io_filetypesue, x_(""), &truename);
2018 		if (f != 0)
2019 		{
2020 			savefp = io_suefilein;
2021 			io_suefilein = f;
2022 			for(i=0; i<MAXLINE; i++) savecurline[i] = io_suecurline[i];
2023 			estrcpy(saveorigline, io_sueorigline);
2024 			estrcpy(savesuelastline, io_suelastline);
2025 			estrcpy(lastprogressmsg, DiaGetTextProgress(dia));
2026 			estrcpy(suevarname, _("Reading "));
2027 			estrcat(suevarname, name);
2028 			estrcat(suevarname, x_("..."));
2029 			DiaSetTextProgress(dia, suevarname);
2030 
2031 			estrcpy(subfilename, name);
2032 			savefilesize = io_suefilesize;
2033 			savelineno = io_suelineno;
2034 			io_suefilesize = filesize(io_suefilein);
2035 			DiaSetProgress(dia, 0, io_suefilesize);
2036 			io_suelineno = 0;
2037 			(void)io_suereadfile(lib, subfilename, dia);
2038 			xclose(io_suefilein);
2039 			io_suefilein = savefp;
2040 			io_suefilesize = savefilesize;
2041 			io_suelineno = savelineno;
2042 			estrcpy(io_suelastline, savesuelastline);
2043 			estrcpy(io_sueorigline, saveorigline);
2044 			for(i=0; i<MAXLINE; i++) io_suecurline[i] = savecurline[i];
2045 			filepos = xtell(io_suefilein);
2046 			DiaSetTextProgress(dia, lastprogressmsg);
2047 			DiaSetProgress(dia, filepos, io_suefilesize);
2048 
2049 			/* now try to find the cell in the library */
2050 			proto = io_suegetnodeproto(lib, subfilename);
2051 			return(proto);
2052 		}
2053 	}
2054 	return(NONODEPROTO);
2055 }
2056 
2057 /*
2058  * Routine to read the next line from file "io_suefilein" and break
2059  * it up into space-separated keywords.  Returns the number
2060  * of keywords (-1 on EOF)
2061  */
io_suegetnextline(CHAR ** keywords,INTBIG curlydepth,void * dia)2062 INTBIG io_suegetnextline(CHAR **keywords, INTBIG curlydepth, void *dia)
2063 {
2064 	CHAR *pt;
2065 	REGISTER INTBIG filepos, keypos, lineno;
2066 	REGISTER BOOLEAN inblank;
2067 
2068 	for(lineno=0; ; lineno++)
2069 	{
2070 		if (io_suelastline[0] == 0)
2071 		{
2072 			if (xfgets(io_suelastline, MAXLINE, io_suefilein)) return(-1);
2073 			io_suelineno++;
2074 			if ((io_suelineno%50) == 0)
2075 			{
2076 				filepos = xtell(io_suefilein);
2077 				DiaSetProgress(dia, filepos, io_suefilesize);
2078 			}
2079 		}
2080 		if (lineno == 0)
2081 		{
2082 			/* first line: use it */
2083 			estrcpy(io_suecurline, io_suelastline);
2084 		} else
2085 		{
2086 			/* subsequent line: use it only if a continuation */
2087 			if (io_suelastline[0] != '+') break;
2088 			estrcat(io_suecurline, &io_suelastline[1]);
2089 		}
2090 		io_suelastline[0] = 0;
2091 	}
2092 	estrcpy(io_sueorigline, io_suecurline);
2093 
2094 	/* parse the line */
2095 	inblank = TRUE;
2096 	keypos = 0;
2097 	for(pt=io_suecurline; *pt != 0; pt++)
2098 	{
2099 		if (*pt == '{') curlydepth++;
2100 		if (*pt == '}') curlydepth--;
2101 		if ((*pt == ' ' || *pt == '\t') && curlydepth == 0)
2102 		{
2103 			*pt = 0;
2104 			inblank = TRUE;
2105 		} else
2106 		{
2107 			if (inblank)
2108 			{
2109 				keywords[keypos++] = pt;
2110 			}
2111 			inblank = FALSE;
2112 		}
2113 	}
2114 	return(keypos);
2115 }
2116 
2117 /*
2118  * Routine to add "directory" to the list of places where SUE files may be found.
2119  */
io_sueadddirectory(CHAR * directory)2120 BOOLEAN io_sueadddirectory(CHAR *directory)
2121 {
2122 	REGISTER INTBIG newnumdir, i;
2123 	CHAR **newdir;
2124 
2125 	newnumdir = io_suenumdirectories + 1;
2126 	newdir = (CHAR **)emalloc(newnumdir * (sizeof (CHAR *)), io_tool->cluster);
2127 	if (newdir == 0) return(TRUE);
2128 	for(i=0; i<io_suenumdirectories; i++)
2129 		newdir[i] = io_suedirectories[i];
2130 	if (allocstring(&newdir[io_suenumdirectories], directory, io_tool->cluster)) return(TRUE);
2131 	if (io_suenumdirectories > 0) efree((CHAR *)io_suedirectories);
2132 	io_suedirectories = newdir;
2133 	io_suenumdirectories = newnumdir;
2134 	return(FALSE);
2135 }
2136 
io_suecleardirectories(void)2137 void io_suecleardirectories(void)
2138 {
2139 	REGISTER INTBIG i;
2140 
2141 	for(i=0; i<io_suenumdirectories; i++)
2142 		efree((CHAR *)io_suedirectories[i]);
2143 	if (io_suenumdirectories > 0) efree((CHAR *)io_suedirectories);
2144 	io_suenumdirectories = 0;
2145 }
2146 
2147 /*************************** OBJECT MANAGEMENT ***************************/
2148 
io_suenewwire(void)2149 SUEWIRE *io_suenewwire(void)
2150 {
2151 	SUEWIRE *sw;
2152 
2153 	if (io_suefreewire != NOSUEWIRE)
2154 	{
2155 		sw = io_suefreewire;
2156 		io_suefreewire = sw->nextsuewire;
2157 	} else
2158 	{
2159 		sw = (SUEWIRE *)emalloc(sizeof(SUEWIRE), io_tool->cluster);
2160 	}
2161 	return(sw);
2162 }
2163 
io_suekillwire(SUEWIRE * sw)2164 void io_suekillwire(SUEWIRE *sw)
2165 {
2166 	sw->nextsuewire = io_suefreewire;
2167 	io_suefreewire = sw;
2168 }
2169 
io_suefreewires(SUEWIRE * firstsw)2170 void io_suefreewires(SUEWIRE *firstsw)
2171 {
2172 	SUEWIRE *sw, *nextsw;
2173 
2174 	for(sw = firstsw; sw != NOSUEWIRE; sw = nextsw)
2175 	{
2176 		nextsw = sw->nextsuewire;
2177 		io_suekillwire(sw);
2178 	}
2179 }
2180 
io_suenewnet(void)2181 SUENET *io_suenewnet(void)
2182 {
2183 	SUENET *sn;
2184 
2185 	if (io_suefreenet != NOSUENET)
2186 	{
2187 		sn = io_suefreenet;
2188 		io_suefreenet = sn->nextsuenet;
2189 	} else
2190 	{
2191 		sn = (SUENET *)emalloc(sizeof(SUENET), io_tool->cluster);
2192 	}
2193 	sn->label = 0;
2194 	return(sn);
2195 }
2196 
io_suekillnet(SUENET * sn)2197 void io_suekillnet(SUENET *sn)
2198 {
2199 	sn->nextsuenet = io_suefreenet;
2200 	io_suefreenet = sn;
2201 	if (sn->label != 0) efree(sn->label);
2202 }
2203 
io_suefreenets(SUENET * firstsn)2204 void io_suefreenets(SUENET *firstsn)
2205 {
2206 	SUENET *sn, *nextsn;
2207 
2208 	for(sn = firstsn; sn != NOSUENET; sn = nextsn)
2209 	{
2210 		nextsn = sn->nextsuenet;
2211 		io_suekillnet(sn);
2212 	}
2213 }
2214