1 /* exhaust.c: crippled pmars-like redcode simulator
2 * $Id: exhaust.c,v 1.3 2003/09/04 15:44:24 varfar Exp $
3 */
4
5 /* This file is part of `exhaust', a memory array redcode simulator.
6 * Copyright (C) 2002 M Joonas Pihlaja
7 *
8 * This file falls under the GNU Public License, version 2. See the
9 * file COPYING for details. */
10
11 #define VERSION 1
12 #define REVISION 9
13 #define PATCHLEVEL 2
14
15 /* Note pmars incompatibility: Minimum warrior separation (minsep) is
16 handled wrong by pmars (prior to 0.8.6) when minsep < MAXLENGTH.
17 This affects running warriors in small cores since the other
18 warriors might be loaded outside of core (it might even crash).
19 */
20 #include <stdio.h>
21 #ifdef SYSV
22 #include <strings.h>
23 #else
24 #include <string.h>
25 #endif
26 #include <stdlib.h>
27 #include <time.h>
28 #include <ctype.h>
29
30 #include "insn.h" /* will */
31 #include "exhaust.h" /* types */
32 #include "asm.h" /* assembler proto */
33 #include "sim.h" /* simulator proto */
34
35 #include "compress.h"
36 #define FALSE (0==1)
37 #define TRUE (1==1)
38
39 static unsigned int NWarriors = 0;
40 static warrior_t *Warriors = NULL; /* [NWarriors] */
41
42 static char **WarriorNames = NULL; /* [NWarriors] */
43 /* File names of warriors. */
44
45 static field_t *Positions = NULL; /* [NWarriors] */
46 static field_t *StartPositions = NULL; /* [NWarriors] */
47 /* Starting position of each warrior.
48 * The StartPositions[] array is a copy
49 * of Positions[], rotated by the number of
50 * the round, to emulate pMARS.
51 */
52
53 static unsigned int *Deaths = NULL; /* [NWarriors] */
54
55 static unsigned **ScoresMatrix = NULL; /* [NWarriors][NWarriors] for each matching */
56
57 static pspace_t **PSpaces = NULL; /* [NWarriors] */
58 /* p-spaces of warriors. */
59
60
61 /* Globals to communicate options from readargs() to main() */
62 int OPT_cycles = 80000; /* cycles until tie */
63 unsigned int OPT_rounds = 1; /* number of rounds to fight */
64 unsigned int OPT_coresize = 8000; /* default core size */
65 unsigned int OPT_minsep = 0; /* minimum distance between warriors */
66 int OPT_processes = 8000; /* default max. procs./warrior */
67 int OPT_F = 0; /* initial position of warrior 2 */
68 int OPT_k = 0; /* nothing (`koth' format flag) */
69 int OPT_b = 0; /* nothing (we are always `brief') */
70 int OPT_m = 0; /* multi-warrior output format. */
71
72 char *prog_name;
73 char errmsg[256];
74
75 /*---------------------------------------------------------------
76 * Utilities
77 */
78
79 #define min(x,y) ((x)<(y) ? (x) : (y))
80
81 /* xbasename(): custom basename(1); returns the filename from a path
82 */
83 static char *
xbasename(char * s)84 xbasename (char *s) {
85 char *b;
86 b = strrchr (s, '/');
87 if (!b) {
88 b = strrchr (s, '\\');
89 }
90 return b ? 1 + b : s;
91 }
92
93 void
panic(const char * msg)94 panic (const char *msg) {
95 fprintf (stderr, "%s: ", prog_name);
96 fprintf (stderr, "%s", msg);
97 exit (1);
98 }
99
100
101 /* pmars/rng.c random number generator
102 */
103 s32_t
rng(s32_t seed)104 rng (s32_t seed) {
105 register s32_t temp = seed;
106 temp = 16807 * (temp % 127773) - 2836 * (temp / 127773);
107 if (temp < 0)
108 temp += 2147483647;
109 return temp;
110 }
111
112 /* Like realloc(3), but works with NULL pointers. */
113 void *
xrealloc(void * p,size_t newsize)114 xrealloc (void *p, size_t newsize) {
115 if (p == NULL) {
116 return malloc (newsize);
117 }
118 return realloc (p, newsize);
119 }
120
121 void
usage()122 usage () {
123 printf ("%s v%d.%d", prog_name, VERSION, REVISION);
124 if (PATCHLEVEL > 0) {
125 printf (".%d", PATCHLEVEL);
126 }
127 printf ("\n");
128 printf ("usage: %s [opts] warriors...\n", prog_name);
129 printf ("opts: -r <rounds>, -c <cycles>, -F <pos>, -s <coresize>,\n"
130 " -p <maxprocs>, -d <minsep>, -bk\n");
131 exit (1);
132 }
133
134 /*---------------------------------------------------------------
135 * Warrior initialisations.
136 */
137
138 void
free_related_memory()139 free_related_memory () {
140 if (Warriors) {
141 free (Warriors);
142 Warriors = NULL;
143 }
144 if (Positions) {
145 free (Positions);
146 Positions = NULL;
147 }
148 if (StartPositions) {
149 free (StartPositions);
150 StartPositions = NULL;
151 }
152 if (Deaths) {
153 free (Deaths);
154 Deaths = NULL;
155 }
156 // free ScoreMatrix..
157 if (PSpaces) {
158 free (PSpaces);
159 PSpaces = NULL;
160 }
161 if (WarriorNames) {
162 free (WarriorNames);
163 WarriorNames = NULL;
164 }
165 }
166
167 void
alloc_related_memory()168 alloc_related_memory () {
169 //unsigned int i;
170
171 Warriors = (warrior_t *) malloc (sizeof (warrior_t) * NWarriors);
172 Positions = (field_t *) malloc (sizeof (field_t) * NWarriors);
173 StartPositions = (field_t *) malloc (sizeof (field_t) * NWarriors);
174 Deaths = (unsigned int *) malloc (sizeof (unsigned int *) * NWarriors);
175
176 // ScoreMatrix..
177
178 /* Warrior P-spaces */
179 PSpaces = (pspace_t **) malloc (sizeof (pspace_t *) * NWarriors);
180
181 }
182
183
184 void
import_warriors()185 import_warriors () {
186 unsigned int i;
187
188 for (i = 0; i < NWarriors; i++) {
189 fprintf(stderr,"loading %s..\n",WarriorNames[i]);
190 asm_fname (WarriorNames[i], &Warriors[i], OPT_coresize);
191 }
192 fprintf(stderr,"%i warriors loaded\n",NWarriors);
193 }
194
195 void
check_sanity()196 check_sanity () {
197 u32_t space_used;
198 unsigned int i;
199
200 /* Make sure each warrior has some code. */
201 for (i = 0; i < NWarriors; i++) {
202 if (Warriors[i].len == 0 || Warriors[i].len > MAXLENGTH) {
203 sprintf (errmsg, "warrior %d has a bad amount of code code (%i instructions)\n", i,Warriors[i].len);
204 panic (errmsg);
205 }
206 }
207
208 /* Make sure there is some minimum sepation. */
209 if (OPT_minsep == 0) {
210 OPT_minsep = min (OPT_coresize / NWarriors, MAXLENGTH);
211 }
212
213 /* Make sure minsep dominates the lengths of all warriors. */
214 for (i = 0; i < NWarriors; i++) {
215 if (OPT_minsep < Warriors[i].len) {
216 sprintf (errmsg, "warrior %s is too long (%i > %i)\n",
217 WarriorNames[i], Warriors[i].len, OPT_minsep);
218 panic (errmsg);
219 }
220 }
221
222 /* Make sure there is space for all warriors to be loaded. */
223 space_used = 2 * OPT_minsep; // two interesting warriors
224 if (space_used > OPT_coresize) {
225 panic ("warriors or minimum separation too large to fit into core\n");
226 }
227 }
228
229
230 /*---------------------------------------------------------------
231 * Warrior positioning algorithms
232 *
233 * These are pMARS compatible. Warrior 0 is always positioned at 0.
234 * posit() and npos() are transcribed from pmars/pos.c. */
235
236 #define RETRIES1 20 /* how many times to try generating one
237 * position */
238 #define RETRIES2 4 /* how many times to start backtracking */
239 int
posit(s32_t * seed)240 posit (s32_t * seed) {
241 unsigned int pos = 1, i;
242 unsigned int retries1 = RETRIES1, retries2 = RETRIES2;
243 int diff;
244
245 do {
246 /* generate */
247 *seed = rng (*seed);
248 Positions[pos] =
249 (*seed % (OPT_coresize - 2 * OPT_minsep + 1)) + OPT_minsep;
250
251 /* test for overlap */
252 for (i = 1; i < pos; ++i) {
253 /* calculate positive difference */
254 diff = (int) Positions[pos] - Positions[i];
255 if (diff < 0)
256 diff = -diff;
257 if ((unsigned int) diff < OPT_minsep)
258 break; /* overlap! */
259 }
260
261 if (i == pos) /* no overlap, generate next number */
262 ++pos;
263 else { /* overlap */
264 if (!retries2)
265 return 1; /* exceeded attempts, fail */
266 if (!retries1) { /* backtrack: generate new sequence starting */
267 pos = i; /* at arbitrary position (last conflict) */
268 --retries2;
269 retries1 = RETRIES1;
270 } else /* generate new current number (pos not
271 * incremented) */
272 --retries1;
273 }
274 } while (pos < NWarriors);
275 return 0;
276 }
277
278 void
npos(s32_t * seed)279 npos (s32_t * seed) {
280 unsigned int i, j;
281 unsigned int temp;
282 unsigned int room = OPT_coresize - OPT_minsep * NWarriors + 1;
283
284 /* Choose NWarriors-1 positions from the available room. */
285 for (i = 1; i < NWarriors; i++) {
286 *seed = rng (*seed);
287 temp = *seed % room;
288 for (j = i - 1; j > 0; j--) {
289 if (temp > Positions[j])
290 break;
291 Positions[j + 1] = Positions[j];
292 }
293 Positions[j + 1] = temp;
294 }
295
296 /* Separate the positions by OPT_minsep cells. */
297 temp = OPT_minsep;
298 for (i = 1; i < NWarriors; i++) {
299 Positions[i] += temp;
300 temp += OPT_minsep;
301 }
302
303 /* Random permutation of positions. */
304 for (i = 1; i < NWarriors; i++) {
305 *seed = rng (*seed);
306 j = *seed % (NWarriors - i) + i;
307 temp = Positions[j];
308 Positions[j] = Positions[i];
309 Positions[i] = temp;
310 }
311 }
312
313 s32_t
compute_positions(s32_t seed)314 compute_positions (s32_t seed) {
315 u32_t avail = OPT_coresize + 1 - NWarriors * OPT_minsep;
316
317 Positions[0] = 0;
318
319 /* Case single warrior. */
320 if (NWarriors == 1)
321 return seed;
322
323 /* Case one on one. */
324 if (NWarriors == 2) {
325 Positions[1] = OPT_minsep + seed % avail;
326 seed = rng (seed);
327 return seed;
328 }
329
330 if (NWarriors > 2) {
331 if (posit (&seed)) {
332 npos (&seed);
333 }
334 }
335 return seed;
336 }
337
338
339 /*---------------------------------------------------------------
340 * Misc.
341 */
342
343 void
save_pspaces()344 save_pspaces () {
345 pspace_t **ps;
346 ps = sim_get_pspaces ();
347 memcpy (PSpaces, ps, sizeof (pspace_t *) * NWarriors);
348 }
349
350 void
amalgamate_pspaces()351 amalgamate_pspaces () {
352 unsigned int i, j;
353
354 /* Share p-space according to PINs. */
355 for (i = 0; i < NWarriors; i++) {
356 if (Warriors[i].have_pin) {
357 for (j = 0; j < i; j++) {
358 if (Warriors[j].have_pin && Warriors[j].pin == Warriors[i].pin) {
359 pspace_share (PSpaces[i], PSpaces[j]);
360 }
361 }
362 }
363 }
364 }
365
366 s32_t
load_warriors(unsigned i,unsigned j,s32_t * seed)367 load_warriors (unsigned i, unsigned j, s32_t * seed) {
368 unsigned pos = OPT_minsep + (*seed % (OPT_coresize - OPT_minsep - OPT_minsep));
369 if(j > NWarriors)
370 exit(1);
371 *seed = rng (*seed);
372 sim_load_warrior (0, &Warriors[i].code[0], Warriors[i].len);
373 sim_load_warrior (pos, &Warriors[j].code[0], Warriors[j].len);
374 if(OPT_coresize <= (pos+Warriors[j].len))
375 exit(1);
376 return pos + Warriors[j].start;
377 }
378
379 void
set_starting_order(unsigned int round)380 set_starting_order (unsigned int round) {
381 unsigned int i;
382 pspace_t **ps;
383
384 /* Copy load positions into starting positions array
385 with a cyclic shift of rounds places. */
386 for (i = 0; i < NWarriors; i++) {
387 unsigned int j = (i + round) % NWarriors;
388 StartPositions[i] = (Positions[j] + Warriors[j].start) % OPT_coresize;
389 }
390
391 /* Copy p-spaces into simulator p-space array with a
392 cyclic shift of rounds places. */
393 ps = sim_get_pspaces ();
394 for (i = 0; i < NWarriors; i++) {
395 ps[i] = PSpaces[(i + round) % NWarriors];
396 }
397 }
398
399 void
output_results(unsigned long long exec_count,time_t run_time)400 output_results (unsigned long long exec_count, time_t run_time) {
401 unsigned int i, j, op, mod;
402 unsigned long long count;
403 char //buf[128],
404 // html
405 *html_preamble = "<html>\n<body>\n<h1>%s</h1>\n",
406 *html_table_preamble = "<h2>%s</h2><table border=\"1\">\n",
407 *html_start_row = "<tr>",
408 *html_name = "<th>%s</th>",
409 *html_text = "<td>%s</td>",
410 *html_value = "<td>%i</td>",
411 *html_valueu32 = "<td>%u</td>",
412 *html_valueu64 = "<td>%llu</td>",
413 *html_end_row = "</tr>\n",
414 *html_table_postamble = "</table>\n",
415 *html_postamble = "</body>\n</html>\n",
416 // tab-seperated-value
417 *tsv_preamble = "",
418 *tsv_table_preamble = "",
419 *tsv_start_row = "",
420 *tsv_name = "%s\t",
421 *tsv_text = tsv_name,
422 *tsv_value = "%i\t",
423 *tsv_valueu32 = "%u\t",
424 *tsv_valueu64 = "%llu\t",
425 *tsv_end_row = "\n", *tsv_table_postamble = "", *tsv_postamble = "",
426 // --- actually used ones ---
427 *preamble = html_preamble,
428 *table_preamble = html_table_preamble,
429 *start_row = html_start_row,
430 *name = html_name,
431 *text = html_text,
432 *value = html_value,
433 *valueu32 = html_valueu32,
434 *valueu64 = html_valueu64,
435 *end_row = html_end_row,
436 *table_postamble = html_table_postamble, *postamble = html_postamble;
437
438 printf (preamble, "RESULTS");
439
440 printf (table_preamble, "Scores Matrix");
441 printf (start_row);
442 printf (name, "");
443 for (i = 0; i < NWarriors; i++) {
444 printf (name, xbasename (WarriorNames[i]));
445 }
446 printf (end_row);
447
448 for (i = 0; i < NWarriors; i++) {
449 printf (start_row);
450 printf (name, xbasename (WarriorNames[i]));
451 for (j = 0; j < NWarriors; j++) {
452 printf (valueu32, ScoresMatrix[i][j]);
453 }
454 printf (end_row);
455 }
456
457 printf (table_postamble);
458
459 printf (table_preamble, "Opcode Execution Frequency");
460 for (op = 0; op < OPCODE_LAST; op++) {
461 for (mod = 0; mod < MODIFIER_LAST; mod++) {
462 count = get_opcode_count (_OP (op, mod));
463 if (count > 0) {
464 printf (start_row);
465 printf (name, MNEMONIC_OPCODE[op]);
466 printf (name, MNEMONIC_MODIFIER[mod]);
467 printf (valueu64, count);
468 printf (end_row);
469 }
470 }
471 }
472 printf (table_postamble);
473
474 printf (table_preamble, "Misc Info");
475 printf (start_row);
476 printf (name, "instructions");
477 printf (valueu64, exec_count);
478 printf(end_row);
479 printf (start_row);
480 printf (name, "checksum");
481 printf (valueu32,get_exec_trail_checksum());
482 printf (end_row);
483 printf (start_row);
484 printf (name, "rounds");
485 printf (valueu64, (NWarriors * (NWarriors / 2) * OPT_rounds));
486 printf(end_row);
487 printf (start_row);
488 printf (name, "time (secs)");
489 printf (valueu32, run_time);
490 printf(end_row);
491 printf (start_row);
492 printf (name, "rounds/sec");
493
494 if (run_time > 0) {
495 printf (valueu64, (NWarriors * (NWarriors / 2) * OPT_rounds) / run_time);
496 } else {
497 printf (text, "all of them!");
498 }
499 printf (end_row);
500
501 printf (start_row);
502 printf (name, "logical bytes");
503 printf (valueu64,get_logical_bytes());
504 printf(end_row);
505 printf (start_row);
506 printf (name, "physical bytes");
507 printf (valueu64,get_physical_bytes());
508 printf(end_row);
509 printf (table_postamble);
510
511 printf (postamble);
512 }
513
open_exec_trail(unsigned i,int append)514 FILE *open_exec_trail(unsigned i,int append) {
515 char exectrailbuf[128];
516 FILE *ret = 0;
517 sprintf (exectrailbuf, "%s.ex", xbasename (WarriorNames[i]));
518 ret = fopen (exectrailbuf, append? "ab": "wb");
519 if (!ret)
520 panic (exectrailbuf);
521 return ret;
522 }
523
524
525 /*---------------------------------------------------------------
526 * Command line arguments.
527 */
528
529 /*
530 * parse options
531 */
532 void
readargs(int argc,char ** argv)533 readargs (int argc, char **argv) {
534 int n, i;
535 char c;
536 int cix;
537 int tmp;
538 FILE *exec_trail;
539
540 n = 1;
541 while (n < argc) {
542 cix = 0;
543 c = argv[n][cix++];
544 if (c == '-' && argv[n][1]) {
545 do {
546 c = argv[n][cix++];
547 if (c)
548 switch (c) {
549
550 case 'k':
551 OPT_k = 1;
552 break;
553 case 'b':
554 OPT_b = 1;
555 break;
556 case 'm':
557 OPT_m = 1;
558 break;
559
560 case 'F':
561 if (n == argc - 1 || !isdigit (argv[n + 1][0]))
562 panic ("bad argument for option -F\n");
563 c = 0;
564 OPT_F = atoi (argv[++n]);
565 break;
566
567 case 's':
568 if (n == argc - 1 || !isdigit (argv[n + 1][0]))
569 panic ("bad argument for option -s\n");
570 c = 0;
571 OPT_coresize = atoi (argv[++n]);
572 if ((int) OPT_coresize <= 0)
573 panic ("core size must be > 0\n");
574 break;
575
576 case 'd':
577 if (n == argc - 1 || !isdigit (argv[n + 1][0]))
578 panic ("bad argument for option -d\n");
579 c = 0;
580 OPT_minsep = atoi (argv[++n]);
581 if ((int) OPT_minsep <= 0)
582 panic ("minimum warrior separation must be > 0\n");
583 break;
584
585 case 'p':
586 if (n == argc - 1 || !isdigit (argv[n + 1][0]))
587 panic ("bad argument for option -p\n");
588 c = 0;
589 OPT_processes = atoi (argv[++n]);
590 if (OPT_processes <= 0)
591 panic ("max processes must be > 0\n");
592 break;
593
594 case 'r':
595 if (n == argc - 1 || !isdigit (argv[n + 1][0]))
596 panic ("bad argument for option -r\n");
597 c = 0;
598 tmp = atoi (argv[++n]);
599 if (tmp < 0)
600 panic ("can't do a negative number of rounds!\n");
601 OPT_rounds = tmp;
602 break;
603 case 'c':
604 if (n == argc - 1 || !isdigit (argv[n + 1][0]))
605 panic ("bad argument for option -c\n");
606 c = 0;
607 OPT_cycles = atoi (argv[++n]);
608 if (OPT_cycles <= 0)
609 panic ("cycles must be > 0\n");
610 break;
611 default:
612 sprintf (errmsg, "unknown option '%c'\n", c);
613 panic (errmsg);
614 }
615 } while (c);
616
617 } else /* it's a file name */ {
618 ++NWarriors;
619 WarriorNames = xrealloc (WarriorNames, NWarriors * sizeof (char *));
620 WarriorNames[NWarriors - 1] = argv[n];
621 }
622 n++;
623 }
624
625 if (NWarriors == 0)
626 usage ();
627 else {
628 ScoresMatrix = (unsigned **) malloc (NWarriors * sizeof (unsigned *));
629 if (!ScoresMatrix)
630 panic ("x5");
631 for (i = 0; i < NWarriors; i++) {
632 exec_trail = open_exec_trail(i,FALSE);
633 if (1 != fwrite (&i, sizeof (i), 1, exec_trail))
634 panic ("x16");
635 fclose(exec_trail); // keep file handle count low!
636 ScoresMatrix[i] = calloc (NWarriors, sizeof (unsigned));
637 if (!ScoresMatrix[i])
638 panic ("x6");
639 }
640 }
641 }
642
643
644
645 /*-------------------------------------------------------------------------
646 * Main
647 */
648
649 int
main(int argc,char ** argv)650 main (int argc, char **argv) {
651 unsigned int i, j, len, ilen, jlen, k, m, // matching values
652 n; /* round counter */
653 s32_t seed; /* rnd seed. */
654 unsigned ok = (0==0);
655 unsigned char exec_trail_op;
656 unsigned long long exec_count = 0;
657 FILE *fi, *fj;
658 time_t start_time, end_time, run_time;
659
660 comp_buf_t desti, destj;
661
662 prog_name = xbasename (argv[0]);
663 readargs (argc, argv);
664 alloc_related_memory ();
665
666 import_warriors ();
667 check_sanity ();
668
669 seed = OPT_F ? OPT_F - OPT_minsep : rng (time (0) * 0x1d872b41);
670
671 /*
672 * Allocate simulator buffers and initialise p-spaces.
673 */
674 if (!sim_alloc_bufs (NWarriors, OPT_coresize, OPT_processes, OPT_cycles))
675 panic ("can't allocate memory.\n");
676
677 save_pspaces ();
678 amalgamate_pspaces (); /* Share P-spaces with equal PINs */
679
680 /*
681 * Fight OPT_rounds rounds.
682 */
683
684 if (OPT_rounds > 0 && NWarriors > 1)
685
686 time (&start_time);
687
688 for (i = 0; i < NWarriors; i++) {
689 fi = open_exec_trail(i,TRUE);
690 desti.f = fi;
691 for (j = i; j < NWarriors; j++) {
692 if (i != j) {
693 fj = open_exec_trail(j,TRUE);
694 destj.f = fj;
695 for (n = 0; n < OPT_rounds; n++) {
696
697 sim_clear_core ();
698
699 switch (sim
700 (2, Warriors[i].start, load_warriors (i, j, &seed),
701 OPT_cycles, NULL)) {
702 case 0: // i won
703 ScoresMatrix[i][j] += 3;
704 break;
705 case 1: // j won
706 ScoresMatrix[j][i] += 3;
707 break;
708 case 2: // tie
709 ScoresMatrix[i][j]++;
710 ScoresMatrix[j][i]++;
711 break;
712 default:
713 panic ("simulator panic!\n");
714 }
715 // assign exec trail
716 len = get_exec_trail_length ();
717 jlen = len >> 1;
718 ilen = jlen + (len & 1);
719 if (ilen + jlen != len)
720 panic ("x16");
721 if (ilen > OPT_cycles)
722 panic ("x13");
723 if (1 != fwrite (&j, sizeof (j), 1, fi))
724 panic ("x14");
725 if (1 != fwrite (&i, sizeof (i), 1, fj))
726 panic ("x15");
727 if (1 != fwrite (&ilen, sizeof (ilen), 1, fi))
728 panic ("x9");
729 if (1 != fwrite (&jlen, sizeof (jlen), 1, fj))
730 panic ("x10");
731 m = 0;
732 desti.len = 0;
733 destj.len = 0;
734 for (k = 0; k < len; k++) {
735 exec_trail_op = get_exec_trail (k);
736 if(exec_trail_op >= _OP(OPCODE_LAST,MODIFIER_LAST))
737 panic ("x17");
738 switch (m++ & 1) { // toggle between the warriors in the round
739 case 0:
740 write_compressed(&desti,exec_trail_op);
741 break;
742 case 1:
743 write_compressed(&destj,exec_trail_op);
744 break;
745 }
746 exec_count++;
747 }
748 flush_compressed(&desti);
749 flush_compressed(&destj);
750 }
751 //printf("%u\r",exec_count); fflush(stdout);
752 fclose(fj);
753 }
754 }
755 fclose(fi);
756 }
757
758 time (&end_time);
759
760 run_time = end_time - start_time;
761
762 sim_free_bufs ();
763 output_results (exec_count, run_time);
764 free_related_memory ();
765 return 0;
766 }
767