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