1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: simalssim.c
6  * Asynchronous Logic Simulator engine
7  * From algorithms by: Brent Serbin and Peter J. Gallant
8  * Last maintained by: Steven M. Rubin
9  *
10  * Copyright (c) 2000 Static Free Software.
11  *
12  * Electric(tm) is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * Electric(tm) is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with Electric(tm); see the file COPYING.  If not, write to
24  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
25  * Boston, Mass 02111-1307, USA.
26  *
27  * Static Free Software
28  * 4119 Alpine Road
29  * Portola Valley, California 94028
30  * info@staticfreesoft.com
31  */
32 
33 #include "config.h"
34 #if SIMTOOL
35 
36 #include "global.h"
37 #include "sim.h"
38 #include "simals.h"
39 
40 static LINKPTR simals_freelink = 0;
41 BOOLEAN  simals_tracing = FALSE;
42 static CHAR *simals_statedesc[] = {N_("High"), N_("Undefined"), N_("Low")};
43 static CHAR *simals_strengthdesc[] = {N_("Off-"), N_("Weak-"), N_("Weak-"), x_(""), x_(""),
44 	N_("Strong-"), N_("Strong-")};
45 
46 /* prototypes for local routines */
47 static BOOLEAN simals_fire_event(void);
48 static void    simals_create_check_list(NODEPTR, LINKPTR);
49 static BOOLEAN simals_schedule_new_events(void);
50 static void    simals_calculate_clock_time(LINKPTR, ROWPTR);
51 static BOOLEAN simals_calculate_event_time(MODPTR, ROWPTR);
52 
53 /*
54  * Routine to free all memory associated with this module.
55  */
simals_freesimmemory(void)56 void simals_freesimmemory(void)
57 {
58 	LINKPTR link;
59 
60 	while (simals_freelink != 0)
61 	{
62 		link = simals_freelink;
63 		simals_freelink = simals_freelink->left;
64 		efree((CHAR *)link);
65 	}
66 }
67 
68 /*
69  * Name: simals_initialize_simulator
70  *
71  * Description:
72  *	This procedure initializes the simulator for a simulation run.  The
73  * vector link list is copied to a master event scheduling link list and
74  * the database is initialized to its starting values.  After these housekeeping
75  * tasks are completed the simulator is ready to start the actual simulation.
76  * Returns the time where the simulation quiesces.
77  */
simals_initialize_simulator(BOOLEAN force)78 double simals_initialize_simulator(BOOLEAN force)
79 {
80 	LINKPTR linkptr2, linkhead, link;
81 	NODEPTR nodehead;
82 	STATPTR stathead;
83 	double tmin, tmax;
84 	REGISTER VARIABLE *var;
85 	double mintime, maxtime;
86 	REGISTER INTBIG tr;
87 	BOOLEAN first;
88 
89 	simals_time_abs = 0.0;
90 	simals_trakptr = simals_trakfull = 0;
91 
92 	while (simals_linkfront)
93 	{
94 		link = simals_linkfront;
95 		if (simals_linkfront->down)
96 		{
97 			simals_linkfront->down->right = simals_linkfront->right;
98 			simals_linkfront = simals_linkfront->down;
99 		} else
100 		{
101 			simals_linkfront = simals_linkfront->right;
102 		}
103 		simals_free_link_mem(link);
104 	}
105 	simals_linkback = 0;
106 
107 	for (linkhead = simals_setroot; linkhead; linkhead = linkhead->right)
108 	{
109 		linkptr2 = simals_alloc_link_mem();
110 		if (linkptr2 == 0) return(simals_time_abs);
111 		linkptr2->type = linkhead->type;
112 		linkptr2->ptr = linkhead->ptr;
113 		linkptr2->state = linkhead->state;
114 		linkptr2->strength = linkhead->strength;
115 		linkptr2->priority = linkhead->priority;
116 		linkptr2->time = linkhead->time;
117 		linkptr2->primhead = 0;
118 		simals_insert_link_list(linkptr2);
119 	}
120 
121 	for (nodehead = simals_noderoot; nodehead; nodehead = nodehead->next)
122 	{
123 		nodehead->sum_state = LOGIC_LOW;
124 		nodehead->sum_strength = OFF_STRENGTH;
125 		nodehead->new_state = LOGIC_LOW;
126 		nodehead->new_strength = OFF_STRENGTH;
127 		nodehead->maxsize = 0;
128 		nodehead->arrive = 0;
129 		nodehead->depart = 0;
130 		nodehead->tk_sec = 0.0;
131 		nodehead->t_last = 0.0;
132 		for (stathead = nodehead->statptr; stathead; stathead = stathead->next)
133 		{
134 			stathead->new_state = LOGIC_LOW;
135 			stathead->new_strength = OFF_STRENGTH;
136 			stathead->sched_op = 0;
137 		}
138 	}
139 
140 	if (simals_seed_flag) srand(3);
141 
142 	/* now run the simulation */
143 	if (force) var = NOVARIABLE; else
144 		var = getvalkey((INTBIG)sim_tool, VTOOL, VINTEGER, simals_no_update_key);
145 	if (var == NOVARIABLE || var->addr == 0)
146 	{
147 		/* determine range of displayed time */
148 		sim_window_inittraceloop();
149 		first = TRUE;
150 		for(;;)
151 		{
152 			tr = sim_window_nexttraceloop();
153 			if (tr == 0) break;
154 			sim_window_gettimerange(tr, &mintime, &maxtime);
155 			if (first)
156 			{
157 				tmin = mintime;
158 				tmax = maxtime;
159 				first = FALSE;
160 			} else
161 			{
162 				if (mintime < tmin) tmin = mintime;
163 				if (maxtime > tmax) tmax = maxtime;
164 			}
165 		}
166 		if (first) return(simals_time_abs);
167 
168 		/* fire events until end of time or quiesced */
169 		while (simals_linkfront && (simals_linkfront->time <= tmax))
170 		{
171 			if (stopping(STOPREASONSIMULATE)) break;
172 			if (simals_fire_event()) break;
173 			if (simals_chekroot)
174 			{
175 				if (simals_schedule_new_events()) break;
176 			}
177 		}
178 
179 		/* redisplay results */
180 		if (simals_levelptr != 0) simals_fill_display_arrays();
181 		sim_window_redraw();
182 		sim_window_updatelayoutwindow();
183 		ttyputverbose(M_("Simulation completed at time %s"),
184 			sim_windowconvertengineeringnotation(simals_time_abs));
185 	}
186 
187 	return(simals_time_abs);
188 }
189 
190 /*
191  * Name: simals_fire_event
192  *
193  * Description:
194  *	This procedure gets the entry from the front of the event scheduling
195  * link list and updates the database accordingly.  If a node is updated by a
196  * user defined vector the node value is changed as specified in the linklist
197  * entry.  If a transition fired, all the output nodes are updated as specified
198  * in the truth table for the transtion.  Returns true on error.
199  */
simals_fire_event(void)200 BOOLEAN simals_fire_event(void)
201 {
202 	INTBIG operand, state;
203 	UCHAR  operatr;
204 	double time;
205 	LINKPTR	linkhead, linkptr2, vecthead;
206 	NODEPTR	nodehead;
207 	ROWPTR rowhead;
208 	STATPTR	stathead;
209 	CHAR s2[300];
210 
211 	simals_time_abs = simals_linkfront->time;
212 	linkhead = simals_linkfront;
213 	if (simals_linkfront->down)
214 	{
215 		simals_linkfront = simals_linkfront->down;
216 		simals_linkfront->left = 0;
217 		simals_linkfront->right = linkhead->right;
218 		simals_linkfront->up = linkhead->up;
219 		if (simals_linkfront->right)
220 		{
221 			simals_linkfront->right->left = simals_linkfront;
222 		} else
223 		{
224 			simals_linkback = simals_linkfront;
225 		}
226 	} else
227 	{
228 		simals_linkfront = simals_linkfront->right;
229 		if (simals_linkfront)
230 		{
231 			simals_linkfront->left = 0;
232 		} else
233 		{
234 			simals_linkback = 0;
235 		}
236 	}
237 
238 	simals_tracing = FALSE;
239 	switch (linkhead->type)
240 	{
241 		case 'G':
242 			stathead = (STATPTR)linkhead->ptr;
243 			if (simals_trace_all_nodes || stathead->nodeptr->tracenode)
244 			{
245 				simals_compute_node_name(stathead->nodeptr, s2);
246 				ttyputmsg(x_("%s: Firing gate %s%s, net %s"), sim_windowconvertengineeringnotation(simals_time_abs),
247 					stathead->primptr->name, stathead->primptr->level, s2);
248 				simals_tracing = TRUE;
249 			}
250 			if (stathead->sched_state != linkhead->state ||
251 				stathead->sched_op != linkhead->operatr ||
252 				stathead->sched_strength != linkhead->strength)
253 			{
254 				break;
255 			}
256 			stathead->sched_op = 0;
257 
258 			operatr = linkhead->operatr;
259 			if (operatr < 128)
260 			{
261 				operand = (INTBIG)linkhead->state;
262 			} else
263 			{
264 				operatr -= 128;
265 				nodehead = (NODEPTR)linkhead->state;
266 				operand = nodehead->sum_state;
267 			}
268 
269 			switch (operatr)
270 			{
271 				case '=':
272 					state = operand;
273 					break;
274 				case '+':
275 					state = stathead->nodeptr->sum_state + operand;
276 					break;
277 				case '-':
278 					state = stathead->nodeptr->sum_state - operand;
279 					break;
280 				case '*':
281 					state = stathead->nodeptr->sum_state * operand;
282 					break;
283 				case '/':
284 					state = stathead->nodeptr->sum_state / operand;
285 					break;
286 				case '%':
287 					state = stathead->nodeptr->sum_state % operand;
288 					break;
289 				default:
290 					ttyputerr(_("Invalid arithmetic operator: %c"), operatr);
291 					return(TRUE);
292 			}
293 
294 			if (state == stathead->new_state &&
295 				linkhead->strength == stathead->new_strength)
296 			{
297 				break;
298 			}
299 			stathead->new_state = state;
300 			stathead->new_strength = linkhead->strength;
301 			simals_create_check_list(stathead->nodeptr, linkhead);
302 			break;
303 
304 		case 'N':
305 			nodehead = (NODEPTR)linkhead->ptr;
306 			if (simals_trace_all_nodes || nodehead->tracenode)
307 			{
308 				simals_compute_node_name(nodehead, s2);
309 				ttyputmsg(x_("%s: Changed state of net %s"), sim_windowconvertengineeringnotation(simals_time_abs), s2);
310 				simals_tracing = TRUE;
311 			}
312 			if (linkhead->state == nodehead->new_state &&
313 				linkhead->strength == nodehead->new_strength)
314 					break;
315 
316 			nodehead->new_state = linkhead->state;
317 			nodehead->new_strength = linkhead->strength;
318 			simals_create_check_list(nodehead, linkhead);
319 			break;
320 
321 		case 'C':
322 			time = simals_time_abs;
323 			rowhead = (ROWPTR)linkhead->ptr;
324 			for (vecthead = (LINKPTR)rowhead->inptr; vecthead; vecthead = vecthead->right)
325 			{
326 				linkptr2 = simals_alloc_link_mem();
327 				if (linkptr2 == 0) return(TRUE);
328 				linkptr2->type = 'N';
329 				linkptr2->ptr = vecthead->ptr;
330 				linkptr2->state = vecthead->state;
331 				linkptr2->strength = vecthead->strength;
332 				linkptr2->priority = vecthead->priority;
333 				linkptr2->time = time;
334 				linkptr2->primhead = 0;
335 				simals_insert_link_list(linkptr2);
336 				time += vecthead->time;
337 			}
338 			if (linkhead->state == 0)
339 			{
340 				simals_calculate_clock_time(linkhead, rowhead);
341 				return(FALSE);
342 			}
343 			--(linkhead->state);
344 			if (linkhead->state)
345 			{
346 				simals_calculate_clock_time(linkhead, rowhead);
347 				return(FALSE);
348 			}
349 	}
350 
351 	simals_free_link_mem(linkhead);
352 	return(FALSE);
353 }
354 
355 /*
356  * Name: simals_create_check_list
357  *
358  * Description:
359  *	This procedure calculates the sum state and strength for a node and if
360  * it has changed from a previous check it will enter the input transition list
361  * into a master check list that is used by the event scheduling routine.
362  * It should be noted that it is neccessary to calculate the sum strength and
363  * state for a node because it is possible to have nodes that have more than
364  * one transition driving it.
365  */
366 
simals_create_check_list(NODEPTR nodehead,LINKPTR linkhead)367 void simals_create_check_list(NODEPTR nodehead, LINKPTR linkhead)
368 {
369 	REGISTER INTBIG state, thisstate;
370 	INTSML	strength, thisstrength;
371 	STATPTR stathead;
372 	TRAKPTR trakhead;
373 	Q_UNUSED( linkhead );
374 
375 	/* get initial state of the node */
376 	state = nodehead->new_state;
377 	strength = nodehead->new_strength;
378 
379 	/* print state of signal if this signal is being traced */
380 	if (simals_tracing)
381 	{
382 		ttyputmsg(x_("  Formerly %s%s, starts at %s%s"),
383 			TRANSLATE(simals_strengthdesc[nodehead->sum_strength]),
384 			TRANSLATE(simals_statedesc[nodehead->sum_state+3]),
385 			TRANSLATE(simals_strengthdesc[strength]),
386 			TRANSLATE(simals_statedesc[state+3]));
387 	}
388 
389 	/* look at all factors affecting the node */
390 	for (stathead = nodehead->statptr; stathead; stathead = stathead->next)
391 	{
392 		thisstate = stathead->new_state;
393 		thisstrength = stathead->new_strength;
394 		if (simals_tracing)
395 			ttyputmsg(x_("    %s%s from %s%s"), TRANSLATE(simals_strengthdesc[thisstrength]),
396 			TRANSLATE(simals_statedesc[thisstate+3]), stathead->primptr->name, stathead->primptr->level);
397 
398 		/* higher strength overrides previous node state */
399 		if (thisstrength > strength)
400 		{
401 			state = thisstate;
402 			strength = thisstrength;
403 			continue;
404 		}
405 
406 		/* same strength: must arbitrate */
407 		if (thisstrength == strength)
408 		{
409 			if (thisstate != state)
410 				state = LOGIC_X;
411 		}
412 	}
413 
414 	/* if the node has nothing driving it, set it to the old value */
415 	if (strength == OFF_STRENGTH)
416 	{
417 		state = nodehead->sum_state;
418 		strength = NODE_STRENGTH;
419 	}
420 
421 	/* stop now if node state did not change */
422 	if (nodehead->sum_state == state && nodehead->sum_strength == strength)
423 	{
424 		if (simals_tracing) ttyputmsg(x_("    NO CHANGE"));
425 		return;
426 	}
427 
428 	if (nodehead->plot_node)
429 	{
430 		trakhead = &(simals_trakroot[simals_trakptr]);
431 		trakhead->ptr = (NODEPTR)nodehead;
432 		trakhead->state = state;
433 		trakhead->strength = strength;
434 		trakhead->time = simals_time_abs;
435 		simals_trakptr = (simals_trakptr + 1) % simals_trace_size;
436 		if (! simals_trakptr)
437 			simals_trakfull = 1;
438 	}
439 	if (simals_tracing)
440 		ttyputmsg(x_("    BECOMES %s%s"), TRANSLATE(simals_strengthdesc[strength]),
441 			TRANSLATE(simals_statedesc[state+3]));
442 
443 	nodehead->sum_state = state;
444 	nodehead->sum_strength = strength;
445 	nodehead->t_last = simals_time_abs;
446 
447 	simals_drive_node = nodehead;
448 	simals_chekroot = nodehead->pinptr;
449 }
450 
451 /*
452  * Name: simals_schedule_new_events
453  *
454  * Description:
455  *	This procedure examines the truth tables for the transitions that are
456  * specified in the checking list.  If there is a match between a truth table
457  * entry and the state of the logic network the transition is scheduled
458  * for firing.  Returns true on error.
459  */
simals_schedule_new_events(void)460 BOOLEAN simals_schedule_new_events(void)
461 {
462 	INTBIG operand;
463 	UCHAR1 flag;
464 	UCHAR operatr;
465 	LOADPTR	chekhead;
466 	MODPTR primhead;
467 	ROWPTR rowhead;
468 	IOPTR iohead;
469 	NODEPTR	nodehead;
470 	FUNCPTR	funchead;
471 
472 	for (chekhead = simals_chekroot; chekhead; chekhead = chekhead->next)
473 	{
474 		primhead = (MODPTR)chekhead->ptr;
475 
476 		if (primhead->type == 'F')
477 		{
478 			funchead = (FUNCPTR)primhead->ptr;
479 			(*(funchead->procptr))(primhead);
480 			continue;
481 		}
482 
483 		for (rowhead = (ROWPTR)primhead->ptr; rowhead; rowhead = rowhead->next)
484 		{
485 			flag = 1;
486 			for (iohead = rowhead->inptr; iohead; iohead = iohead->next)
487 			{
488 				operatr = iohead->operatr;
489 				if (operatr < 128)
490 				{
491 					operand = (INTBIG)iohead->operand;
492 				} else
493 				{
494 					operatr -= 128;
495 					nodehead = (NODEPTR)iohead->operand;
496 					operand = nodehead->sum_state;
497 				}
498 
499 				switch (operatr)
500 				{
501 					case '=':
502 						if (iohead->nodeptr->sum_state != operand) flag = 0;
503 						break;
504 					case '!':
505 						if (iohead->nodeptr->sum_state == operand) flag = 0;
506 						break;
507 					case '<':
508 						if (iohead->nodeptr->sum_state >= operand) flag = 0;
509 						break;
510 					case '>':
511 						if (iohead->nodeptr->sum_state <= operand) flag = 0;
512 						break;
513 					default:
514 						ttyputerr(_("Invalid logical operator: %c"), operatr);
515 						return(TRUE);
516 				}
517 
518 				if (! flag) break;
519 			}
520 
521 			if (flag)
522 			{
523 				if (simals_calculate_event_time(primhead, rowhead)) return(TRUE);
524 				break;
525 			}
526 		}
527 	}
528 	simals_chekroot = 0;
529 	return(FALSE);
530 }
531 
532 /*
533  * Name: simals_calculate_clock_time
534  *
535  * Description:
536  *	This procedure calculates the time when the next occurance of a set of
537  * clock vectors is to be added to the event scheduling linklist.
538  *
539  * Calling Arguments:
540  *	linkhead = pointer to the link element to be reinserted into the list
541  *  rowhead  = pointer to a row element containing timing information
542  */
simals_calculate_clock_time(LINKPTR linkhead,ROWPTR rowhead)543 void simals_calculate_clock_time(LINKPTR linkhead, ROWPTR rowhead)
544 {
545 	double time, prob;
546 
547 	time = simals_time_abs;
548 
549 	if (rowhead->delta) time += rowhead->delta;
550 	if (rowhead->linear)
551 	{
552 		prob = rand() / MAXINTBIG;
553 		time += 2.0 * prob * rowhead->linear;
554 	}
555 
556 	/*
557 	 * if (rowhead->exp)
558 	 * {
559 	 * 	prob = rand() / MAXINTBIG;
560 	 * 	time += (-log(prob) * (rowhead->exp));
561 	 * }
562 	 */
563 
564 	linkhead->time = time;
565 	simals_insert_link_list(linkhead);
566 
567 	return;
568 }
569 
570 /*
571  * Name: simals_calculate_event_time
572  *
573  * Description:
574  *	This procedure calculates the time of occurance of an event and then
575  * places an entry into the event scheduling linklist for later execution.
576  * Returns true on error.
577  *
578  * Calling Arguments:
579  *	primhead  = pointer to the primitive to be scheduled for firing
580  *	rowhead  = pointer to the row containing the event to be scheduled
581  */
simals_calculate_event_time(MODPTR primhead,ROWPTR rowhead)582 BOOLEAN simals_calculate_event_time(MODPTR primhead, ROWPTR rowhead)
583 {
584 	INTSML priority;
585 	double time, prob;
586 	STATPTR	stathead;
587 	IOPTR iohead;
588 	LINKPTR	linkptr2;
589 
590 	time = 0.0;
591 	priority = primhead->priority;
592 
593 	if (rowhead->delta) time += rowhead->delta;
594 	if (rowhead->abs) time += rowhead->abs;
595 	if (rowhead->linear)
596 	{
597 		prob = rand() / MAXINTBIG;
598 		time += 2.0 * prob * rowhead->linear;
599 	}
600 
601 	/*
602 	 * if (rowhead->exp)
603 	 * {
604 	 * 	prob = rand() / MAXINTBIG;
605 	 * 	time += (-log(prob) * (rowhead->exp));
606 	 * }
607 	 */
608 
609 	if (rowhead->random)
610 	{
611 		prob = rand() / MAXINTBIG;
612 		if (prob <= rowhead->random)
613 		{
614 			priority = -1;
615 		}
616 	}
617 
618 	if (primhead->fanout)
619 	{
620 		stathead = (STATPTR)rowhead->outptr->nodeptr;
621 		time *= stathead->nodeptr->load;
622 	}
623 	time += simals_time_abs;
624 
625 	for (iohead = rowhead->outptr; iohead; iohead = iohead->next)
626 	{
627 		stathead = (STATPTR)iohead->nodeptr;
628 		if (stathead->sched_op == iohead->operatr &&
629 			stathead->sched_state == (INTBIG)iohead->operand &&
630 				stathead->sched_strength == iohead->strength)
631 		{
632 			continue;
633 		}
634 
635 		linkptr2 = simals_alloc_link_mem();
636 		if (linkptr2 == 0) return(TRUE);
637 		linkptr2->type = 'G';
638 		linkptr2->ptr = (CHAR *)stathead;
639 		linkptr2->operatr = stathead->sched_op = iohead->operatr;
640 		linkptr2->state = stathead->sched_state = (INTBIG)iohead->operand;
641 		linkptr2->strength = stathead->sched_strength = iohead->strength;
642 		linkptr2->time = time;
643 		linkptr2->priority = priority;
644 		linkptr2->primhead = primhead;
645 		if (simals_tracing)
646 		{
647 			ttyputmsg(_("      Schedule(G): %s%s at %s"), stathead->primptr->name,
648 				stathead->primptr->level, sim_windowconvertengineeringnotation(time));
649 		}
650 		simals_insert_link_list(linkptr2);
651 	}
652 	return(FALSE);
653 }
654 
655 /*
656  * Name: simals_alloc_link_mem
657  *
658  * Description:
659  *	This procedure allocates a block of memory for a single entry in the
660  * link list data structure.  This procedure uses its own memory pool if some is
661  * available to speed up the simulation process.  Returns zero on error.
662  */
simals_alloc_link_mem(void)663 LINKPTR simals_alloc_link_mem(void)
664 {
665 	LINKPTR link;
666 
667 	if (simals_freelink == 0)
668 	{
669 		link = (LINKPTR)simals_alloc_mem((INTBIG)sizeof(LINK));
670 		if (link == 0) return(0);
671 	} else
672 	{
673 		link = simals_freelink;
674 		simals_freelink = simals_freelink->left;
675 	}
676 	return(link);
677 }
678 
679 /*
680  * Name: simals_free_link_mem
681  *
682  * Description:
683  *	This procedure frees a block of memory for a link list data structure.
684  * Rather than returning the memory to the system it enters the memory location
685  * inot its own memory pool.  This is done to speed up the performance of a
686  * simulation.
687  *
688  * Calling Argument
689  *	linkhead = pointer to data structure that should be added to memory pool
690  */
simals_free_link_mem(LINKPTR linkhead)691 void simals_free_link_mem(LINKPTR linkhead)
692 {
693 	linkhead->left = simals_freelink;
694 	simals_freelink = linkhead;
695 }
696 
697 /*
698  * Name: simals_insert_set_list
699  *
700  * Description:
701  *	This procedure inserts a data element into a linklist that is sorted
702  * by time and then priority.  This link list is used to schedule events
703  * for the simulation.
704  *
705  * Calling Arguments:
706  *	linkhead = pointer to the data element that is going to be inserted
707  */
simals_insert_set_list(LINKPTR linkhead)708 CHAR **simals_insert_set_list(LINKPTR linkhead)
709 {
710 	CHAR **linkptr1;
711 	LINKPTR linkptr2;
712 
713 	linkptr1 = (CHAR **)&simals_setroot;
714 	for(;;)
715 	{
716 		if ((*linkptr1) == 0)
717 		{
718 			*linkptr1 = (CHAR *)linkhead;
719 			break;
720 		}
721 		linkptr2 = (LINKPTR)*linkptr1;
722 		if (linkptr2->time > linkhead->time || (linkptr2->time == linkhead->time &&
723 			linkptr2->priority > linkhead->priority))
724 		{
725 			linkhead->right = linkptr2;
726 			*linkptr1 = (CHAR *)linkhead;
727 			break;
728 		}
729 		linkptr1 = (CHAR **)&(linkptr2->right);
730 	}
731 	return(linkptr1);
732 }
733 
734 /*
735  * Name: simals_insert_link_list
736  *
737  * Description:
738  *	This procedure inserts a data element into a linklist that is 2
739  * dimensionally sorted first by time and then priority.  This link list is
740  * used to schedule events for the simulation.
741  *
742  * Calling Arguments:
743  *	linkhead = pointer to the data element that is going to be inserted
744  */
simals_insert_link_list(LINKPTR linkhead)745 void simals_insert_link_list(LINKPTR linkhead)
746 {
747 	CHAR **linkptr1;
748 	LINKPTR linkptr2, linkptr3;
749 
750 	linkptr1 = (CHAR **)&simals_linkback;
751 	linkptr2 = simals_linkback;
752 	linkptr3 = 0;
753 	for(;;)
754 	{
755 		if (! linkptr2)
756 		{
757 			simals_linkfront = (LINKPTR)(*linkptr1 = (CHAR *)linkhead);
758 			linkhead->left = 0;
759 			linkhead->right = linkptr3;
760 			linkhead->up = linkhead;
761 			linkhead->down = 0;
762 			return;
763 		}
764 
765 		if (linkptr2->time < linkhead->time)
766 		{
767 			*linkptr1 = (CHAR *)(linkptr2->right = linkhead);
768 			linkhead->left = linkptr2;
769 			linkhead->right = linkptr3;
770 			linkhead->up = linkhead;
771 			linkhead->down = 0;
772 			return;
773 		}
774 
775 		if (linkptr2->time == linkhead->time)
776 		{
777 			if (linkptr2->priority > linkhead->priority)
778 			{
779 				linkhead->left = linkptr2->left;
780 				linkhead->right = linkptr2->right;
781 				linkhead->down = linkptr2;
782 				linkhead->up = linkptr2->up;
783 				linkptr2->up = (LINKPTR)(*linkptr1 = (CHAR *)linkhead);
784 				if (linkhead->left)
785 				{
786 					linkhead->left->right = linkhead;
787 				} else
788 				{
789 					simals_linkfront = linkhead;
790 				}
791 				return;
792 			}
793 
794 			linkptr1 = (CHAR **)&(linkptr2->up);
795 			linkptr2 = linkptr2->up;
796 			linkptr3 = 0;
797 			for(;;)
798 			{
799 				if (linkptr2->priority <= linkhead->priority)
800 				{
801 					*linkptr1 = (CHAR *)(linkptr2->down = linkhead);
802 					linkhead->up = linkptr2;
803 					linkhead->down = linkptr3;
804 					return;
805 				}
806 
807 				linkptr3 = linkptr2;
808 				linkptr1 = (CHAR **)&(linkptr2->up);
809 				linkptr2 = linkptr2->up;
810 			}
811 		}
812 
813 		linkptr3 = linkptr2;
814 		linkptr1 = (CHAR **)&(linkptr2->left);
815 		linkptr2 = linkptr2->left;
816 	}
817 }
818 
819 #endif  /* SIMTOOL - at top */
820