1 /*
2 * Copyright (C) 2003-2020 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * GXemul's main entry point.
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
37
38 #include "ComponentFactory.h"
39 #include "console.h"
40 #include "cpu.h"
41 #include "debugger.h"
42 #include "device.h"
43 #include "diskimage.h"
44 #include "emul.h"
45 #include "GXemul.h"
46 #include "machine.h"
47 #include "misc.h"
48 #include "settings.h"
49 #include "timer.h"
50 #include "UnitTest.h"
51
52
53 extern int single_step;
54 extern int force_debugger_at_exit;
55
56 extern int optind;
57 extern char *optarg;
58
59 struct settings *global_settings;
60
61 int extra_argc;
62 char **extra_argv;
63 char *progname;
64
65 size_t dyntrans_cache_size = DEFAULT_DYNTRANS_CACHE_SIZE;
66 static int skip_srandom_call = 0;
67
68
69 /*****************************************************************************
70 *
71 * NOTE: debug(), fatal(), and debug_indentation() are not re-entrant.
72 * The global variable quiet_mode can be used to suppress the output
73 * of debug(), but not the output of fatal().
74 *
75 *****************************************************************************/
76
77 int verbose = 0;
78 int quiet_mode = 0;
79
80 static int debug_indent = 0;
81 static int debug_currently_at_start_of_line = 1;
82
83
84 /*
85 * va_debug():
86 *
87 * Used internally by debug() and fatal().
88 */
va_debug(va_list argp,const char * fmt)89 static void va_debug(va_list argp, const char *fmt)
90 {
91 char buf[DEBUG_BUFSIZE + 1];
92 char *s;
93 int i;
94
95 buf[0] = buf[DEBUG_BUFSIZE] = 0;
96 vsnprintf(buf, DEBUG_BUFSIZE, fmt, argp);
97
98 s = buf;
99 while (*s) {
100 if (debug_currently_at_start_of_line) {
101 for (i=0; i<debug_indent; i++)
102 printf(" ");
103 }
104
105 printf("%c", *s);
106
107 debug_currently_at_start_of_line = 0;
108 if (*s == '\n' || *s == '\r')
109 debug_currently_at_start_of_line = 1;
110 s++;
111 }
112 }
113
114
115 /*
116 * debug_indentation():
117 *
118 * Modify the debug indentation.
119 */
debug_indentation(int diff)120 void debug_indentation(int diff)
121 {
122 debug_indent += diff;
123 if (debug_indent < 0)
124 fprintf(stderr, "WARNING: debug_indent less than 0!\n");
125 }
126
127
128 /*
129 * debug():
130 *
131 * Debug output (ignored if quiet_mode is set).
132 */
debug(const char * fmt,...)133 void debug(const char *fmt, ...)
134 {
135 va_list argp;
136
137 if (quiet_mode)
138 return;
139
140 va_start(argp, fmt);
141 va_debug(argp, fmt);
142 va_end(argp);
143 }
144
145
146 /*
147 * fatal():
148 *
149 * Fatal works like debug(), but doesn't care about the quiet_mode
150 * setting.
151 */
fatal(const char * fmt,...)152 void fatal(const char *fmt, ...)
153 {
154 va_list argp;
155
156 va_start(argp, fmt);
157 va_debug(argp, fmt);
158 va_end(argp);
159 }
160
161
162 /*****************************************************************************/
163
164
165 /*
166 * internal_w():
167 *
168 * For internal use by gxemul itself.
169 */
internal_w(char * arg)170 void internal_w(char *arg)
171 {
172 if (arg == NULL || strncmp(arg, "W@", 2) != 0) {
173 fprintf(stderr, "-W is for internal use by gxemul,"
174 " not for manual use.\n");
175 exit(1);
176 }
177
178 arg += 2;
179
180 switch (arg[0]) {
181 case 'C':
182 GXemul::GenerateHTMLListOfComponents(false);
183 exit(0);
184 break;
185 case 'D':
186 GXemul::DumpMachineAsHTML(arg + 1);
187 break;
188 case 'S':
189 console_slave(arg + 1);
190 break;
191 case 'M':
192 GXemul::GenerateHTMLListOfComponents(true);
193 exit(0);
194 break;
195 case 'U':
196 {
197 int result = UnitTest::RunTests();
198
199 // Hack to prevent leaks:
200 ComponentFactory::UnregisterAllComponentClasses();
201
202 #ifndef NDEBUG
203 int leaks = check_leaks();
204 if (leaks > 0) {
205 cerr << "Having memory leaks counts as failure to run the tests!\n";
206 exit(1);
207 }
208 #endif
209
210 exit(result);
211 }
212 break;
213 default:
214 fprintf(stderr, "internal_w(): UNIMPLEMENTED arg = '%s'\n",
215 arg);
216 }
217 }
218
219
220 /*****************************************************************************/
221
222
223 /*
224 * usage():
225 *
226 * Prints program usage to stdout.
227 */
usage(int longusage)228 static void usage(int longusage)
229 {
230
231 printf("GXemul " VERSION" " COPYRIGHT_MSG"\n" SECONDARY_MSG);
232 printf("Read the source code and/or documentation for other Copyright messages.\n");
233
234 printf("\nUsage: %s [machine, other, and general options] [file [...]]\n", progname);
235 printf(" or %s [general options] @configfile\n", progname);
236
237 if (!longusage) {
238 printf("\nRun %s -h for help on command line options.\n", progname);
239 return;
240 }
241
242 printf("\nMachine selection options:\n");
243 printf(" -E t try to emulate machine type t. (Use -H to get "
244 "a list of types.)\n");
245 printf(" -e st try to emulate machine subtype st. (Use this "
246 "with -E.)\n");
247
248 printf("\nOther options:\n");
249 printf(" -C x try to emulate a specific CPU. (Use -H to get a "
250 "list of types.)\n");
251 printf(" -d fname add fname as a disk image. You can add \"xxx:\""
252 " as a prefix\n");
253 printf(" where xxx is one or more of the following:\n");
254 printf(" b specifies that this is the boot"
255 " device\n");
256 printf(" c CD-ROM\n");
257 printf(" d DISK\n");
258 printf(" f FLOPPY\n");
259 printf(" gH;S; set geometry to H heads and S"
260 " sectors-per-track\n");
261 printf(" i IDE\n");
262 printf(" oOFS; set base offset to OFS (for ISO9660"
263 " filesystems)\n");
264 printf(" r read-only (don't allow changes to the"
265 " file)\n");
266 printf(" s SCSI\n");
267 printf(" t tape\n");
268 printf(" V add an overlay\n");
269 printf(" 0-7 force a specific ID\n");
270 printf(" -I hz set the main cpu frequency to hz (not used by "
271 "all combinations\n of machines and guest OSes)\n");
272 printf(" -i display each instruction as it is executed\n");
273 printf(" -J disable dyntrans instruction combinations\n");
274 printf(" -j name set the name of the kernel; for DECstation "
275 "emulation, this passes\n the name to the bootloader,"
276 " for example:\n");
277 printf(" -j netbsd (NetBSD/pmax) "
278 "-j bsd (OpenBSD/pmax)\n");
279 printf(" -j vmsprite (Sprite/pmax) "
280 "-j vmunix (Ultrix/RISC)\n");
281 printf(" For other emulation modes, if the boot disk is an"
282 " ISO9660\n filesystem, -j sets the name of the"
283 " kernel to load.\n");
284 printf(" -M m emulate m MBs of physical RAM\n");
285 printf(" -N display nr of instructions/second average, at"
286 " regular intervals\n");
287 printf(" -n nr set nr of CPUs (for SMP experiments)\n");
288 printf(" -O force netboot (tftp instead of disk), even when"
289 " a disk image is\n"
290 " present (for DECstation, SGI, and ARC emulation)\n");
291 printf(" -o arg set the boot argument, for DEC, ARC, or SGI"
292 " emulation\n");
293 printf(" (default arg for DEC is -a, for ARC/SGI -aN)\n");
294 printf(" -p pc add a breakpoint (remember to use the '0x' "
295 "prefix for hex!)\n");
296 printf(" -Q no built-in PROM emulation (use this for "
297 "running ROM images)\n");
298 printf(" -R use random bootstrap cpu, instead of nr 0\n");
299 printf(" -r register dumps before every instruction\n");
300 printf(" -S initialize emulated RAM to random bytes, "
301 "instead of zeroes\n");
302 printf(" -s f:name write statistics to file 'name', "
303 "f is one or more of the following:\n");
304 printf(" v virtual program counter\n");
305 printf(" p physical equivalent of program counter\n");
306 printf(" i internal ic->f representation of "
307 "the program counter\n");
308 printf(" and optionally:\n");
309 printf(" d disable statistics gathering at "
310 "startup\n");
311 printf(" o overwrite instead of append\n");
312 printf(" -T halt on non-existant memory accesses\n");
313 printf(" -t show function trace tree\n");
314 printf(" -U enable slow_serial_interrupts_hack_for_linux\n");
315 #ifdef WITH_X11
316 printf(" -X use X11\n");
317 printf(" -x open up new xterms for emulated serial ports "
318 "(default is on when\n using configuration files or"
319 " when X11 is used, off otherwise)\n");
320 printf(" -Y n scale down framebuffer windows by n x n times\n");
321 #endif /* WITH_X11 */
322 printf(" -Z n set nr of graphics cards, for emulating a "
323 "dual-head or tripple-head\n"
324 " environment (only for DECstation emulation)\n");
325 #ifdef WITH_X11
326 printf(" -z disp add disp as an X11 display to use for framebuffers\n");
327 #endif /* WITH_X11 */
328
329 printf("\nGeneral options:\n");
330 printf(" -c cmd add cmd as a command to run before starting "
331 "the simulation\n");
332 printf(" -D skip the srandom call at startup\n");
333 printf(" -H display a list of possible CPU and "
334 "machine types\n");
335 printf(" -h display this help message\n");
336 printf(" -k n set dyntrans translation caches to n MB (default"
337 " size is %i MB)\n", DEFAULT_DYNTRANS_CACHE_SIZE / 1048576);
338 printf(" -K force the debugger to be entered at the end "
339 "of a simulation\n");
340 printf(" -q quiet mode (don't print startup messages)\n");
341 printf(" -V start up in the single-step debugger, paused\n");
342 printf(" -v increase debug message verbosity\n");
343 printf("\n");
344 printf("If you are selecting a machine type to emulate directly "
345 "on the command line,\nthen you must specify one or more names"
346 " of files that you wish to load into\n"
347 "memory. Supported formats are: ELF a.out ecoff srec syms raw\n"
348 "where syms is the text produced by running 'nm' (or 'nm -S') "
349 "on a binary.\n"
350 "To load a raw binary into memory, add \"address:\" in front "
351 "of the filename,\n"
352 "or \"address:skiplen:\" or \"address:skiplen:initialpc:\".\n"
353 "\nExamples:\n"
354 " 0xbfc00000:rom.bin for a raw ROM image\n"
355 " 0xbfc00000:0x100:rom.bin for an image with "
356 "0x100 bytes header\n"
357 " 0xbfc00000:0x100:0xbfc00884:rom.bin "
358 "start with pc=0xbfc00884\n\n");
359
360 printf("\n\"New framework\" options (experimental):\n");
361 printf("\nUsage: %s [options] -e name [additional components and files [...]]\n", progname);
362 printf(" or %s [options] configfile\n", progname);
363 printf(" or %s -H\n", progname);
364 printf(" or %s -V\n", progname);
365 printf("\n");
366 printf(" -B Enable snapshotting (reverse stepping support).\n");
367 printf(" -H Display a list of available machine templates.\n");
368 printf(" -e name Start with a machine based on template 'name'.\n");
369 printf(" -q Quiet mode (suppress debug messages).\n");
370 printf(" -V Start up in interactive debugging mode, paused.\n");
371 printf("\n");
372 }
373
374
375 /*
376 * get_cmd_args():
377 *
378 * Reads command line arguments.
379 */
get_cmd_args(int argc,char * argv[],struct emul * emul,char *** diskimagesp,int * n_diskimagesp)380 int get_cmd_args(int argc, char *argv[], struct emul *emul,
381 char ***diskimagesp, int *n_diskimagesp)
382 {
383 int ch, res, using_switch_d = 0, using_switch_Z = 0;
384 int using_switch_e = 0, using_switch_E = 0;
385 bool using_switch_B = false;
386 char *type = NULL, *subtype = NULL;
387 int msopts = 0; /* Machine-specific options used */
388 struct machine *m = emul_add_machine(emul, NULL);
389
390 const char *opts =
391 "BC:c:Dd:E:e:HhI:iJj:k:KM:Nn:Oo:p:QqRrSs:TtUVvW:"
392 #ifdef WITH_X11
393 "XxY:"
394 #endif
395 "Z:z:";
396
397 while ((ch = getopt(argc, argv, opts)) != -1) {
398 switch (ch) {
399 case 'B':
400 using_switch_B = true;
401 break;
402 case 'C':
403 CHECK_ALLOCATION(m->cpu_name = strdup(optarg));
404 msopts = 1;
405 break;
406 case 'c':
407 emul->n_debugger_cmds ++;
408 CHECK_ALLOCATION(emul->debugger_cmds = (char **)
409 realloc(emul->debugger_cmds,
410 emul->n_debugger_cmds * sizeof(char *)));
411 CHECK_ALLOCATION(emul->debugger_cmds[emul->
412 n_debugger_cmds-1] = strdup(optarg));
413 break;
414 case 'D':
415 skip_srandom_call = 1;
416 break;
417 case 'd':
418 /* diskimage_add() is called further down */
419 (*n_diskimagesp) ++;
420 CHECK_ALLOCATION( (*diskimagesp) = (char **)
421 realloc(*diskimagesp,
422 sizeof(char *) * (*n_diskimagesp)) );
423 CHECK_ALLOCATION( (*diskimagesp)[(*n_diskimagesp) - 1] =
424 strdup(optarg) );
425 using_switch_d = 1;
426 msopts = 1;
427 break;
428 case 'E':
429 if (using_switch_E ++ > 0) {
430 fprintf(stderr, "-E already used.\n");
431 exit(1);
432 }
433 type = optarg;
434 msopts = 1;
435 break;
436 case 'e':
437 if (using_switch_e ++ > 0) {
438 fprintf(stderr, "-e already used.\n");
439 exit(1);
440 }
441 subtype = optarg;
442 msopts = 1;
443 break;
444 case 'H':
445 GXemul::ListTemplates();
446 printf("--------------------------------------------------------------------------\n\n");
447 printf("The following applies to the LEGACY modes only:\n\n");
448 machine_list_available_types_and_cpus();
449 exit(1);
450 case 'h':
451 usage(1);
452 exit(1);
453 case 'I':
454 m->emulated_hz = atoi(optarg);
455 msopts = 1;
456 break;
457 case 'i':
458 m->instruction_trace = 1;
459 msopts = 1;
460 break;
461 case 'J':
462 m->allow_instruction_combinations = 0;
463 msopts = 1;
464 break;
465 case 'j':
466 CHECK_ALLOCATION(m->boot_kernel_filename = strdup(optarg));
467 msopts = 1;
468 break;
469 case 'k':
470 dyntrans_cache_size = atoi(optarg) * 1048576;
471 if (dyntrans_cache_size < 1) {
472 fprintf(stderr, "The dyntrans cache size must"
473 " be at least 1 MB.\n");
474 exit(1);
475 }
476 break;
477 case 'K':
478 force_debugger_at_exit = 1;
479 break;
480 case 'M':
481 m->physical_ram_in_mb = atoi(optarg);
482 msopts = 1;
483 break;
484 case 'N':
485 m->show_nr_of_instructions = 1;
486 msopts = 1;
487 break;
488 case 'n':
489 m->ncpus = atoi(optarg);
490 msopts = 1;
491 break;
492 case 'O':
493 m->force_netboot = 1;
494 msopts = 1;
495 break;
496 case 'o':
497 CHECK_ALLOCATION(m->boot_string_argument = strdup(optarg));
498 msopts = 1;
499 break;
500 case 'p':
501 machine_add_breakpoint_string(m, optarg);
502 msopts = 1;
503 break;
504 case 'Q':
505 m->prom_emulation = 0;
506 msopts = 1;
507 break;
508 case 'q':
509 quiet_mode = 1;
510 break;
511 case 'R':
512 m->use_random_bootstrap_cpu = 1;
513 msopts = 1;
514 break;
515 case 'r':
516 m->register_dump = 1;
517 msopts = 1;
518 break;
519 case 'S':
520 m->random_mem_contents = 1;
521 msopts = 1;
522 break;
523 case 's':
524 machine_statistics_init(m, optarg);
525 msopts = 1;
526 break;
527 case 'T':
528 m->halt_on_nonexistant_memaccess = 1;
529 msopts = 1;
530 break;
531 case 't':
532 m->show_trace_tree = 1;
533 msopts = 1;
534 break;
535 case 'U':
536 m->slow_serial_interrupts_hack_for_linux = 1;
537 msopts = 1;
538 break;
539 case 'V':
540 single_step = ENTER_SINGLE_STEPPING;
541 break;
542 case 'v':
543 verbose ++;
544 break;
545 case 'W':
546 internal_w(optarg);
547 exit(0);
548 case 'X':
549 m->x11_md.in_use = 1;
550 msopts = 1;
551 /* FALL-THROUGH */
552 case 'x':
553 console_allow_slaves(1);
554 break;
555 case 'Y':
556 m->x11_md.scaledown = atoi(optarg);
557 if (m->x11_md.scaledown < -1) {
558 m->x11_md.scaleup = - m->x11_md.scaledown;
559 m->x11_md.scaledown = 1;
560 }
561 if (m->x11_md.scaledown < 1) {
562 fprintf(stderr, "Invalid scaledown value.\n");
563 exit(1);
564 }
565 msopts = 1;
566 break;
567 case 'Z':
568 m->n_gfx_cards = atoi(optarg);
569 using_switch_Z = 1;
570 msopts = 1;
571 break;
572 case 'z':
573 m->x11_md.n_display_names ++;
574 CHECK_ALLOCATION(m->x11_md.display_names = (char **) realloc(
575 m->x11_md.display_names,
576 m->x11_md.n_display_names * sizeof(char *)));
577 CHECK_ALLOCATION(m->x11_md.display_names[
578 m->x11_md.n_display_names-1] = strdup(optarg));
579 msopts = 1;
580 break;
581 default:
582 fprintf(stderr, "Run %s -h for help on command "
583 "line options.\n", progname);
584 exit(1);
585 }
586 }
587
588 argc -= optind;
589 argv += optind;
590
591 extra_argc = argc;
592 extra_argv = argv;
593
594 // If -V is used, -q is ignored.
595 if (single_step == ENTER_SINGLE_STEPPING)
596 quiet_mode = 0;
597
598 if (type == NULL && subtype == NULL &&
599 (single_step == ENTER_SINGLE_STEPPING || (argc > 0 && argv[0][0] != '@'))) {
600 int res2 = 0;
601 {
602 GXemul gxemul;
603 gxemul.InitUI();
604
605 if (single_step == ENTER_SINGLE_STEPPING)
606 gxemul.SetRunState(GXemul::Paused);
607 else
608 gxemul.SetRunState(GXemul::Running);
609
610 gxemul.SetSnapshottingEnabled(using_switch_B);
611
612 if (quiet_mode)
613 gxemul.SetQuietMode(true);
614
615 if (argc > 0 && !gxemul.ParseFilenames("", argc, argv))
616 res = 1;
617
618 if (res2 == 0)
619 res2 = gxemul.Run();
620 }
621
622 // Note: exit() is outside the GXemul scope, so that GXemul's
623 // destructor runs.
624 exit(res2);
625 }
626
627 if (type != NULL || subtype != NULL) {
628 if (type == NULL)
629 type = strdup("");
630 if (subtype == NULL)
631 subtype = strdup("");
632
633 /* Is it a new machine mode? */
634 if (subtype[0] != '\0') {
635 int res2 = 0;
636 bool doExit = false;
637
638 {
639 GXemul gxemul;
640 gxemul.InitUI();
641
642 if (single_step == ENTER_SINGLE_STEPPING)
643 gxemul.SetRunState(GXemul::Paused);
644 else
645 gxemul.SetRunState(GXemul::Running);
646
647 gxemul.SetSnapshottingEnabled(using_switch_B);
648
649 if (quiet_mode)
650 gxemul.SetQuietMode(true);
651
652 if (gxemul.IsTemplateMachine(subtype)) {
653 if (!gxemul.ParseFilenames(subtype, argc, argv))
654 res2 = 1;
655
656 if (res2 == 0)
657 res2 = gxemul.Run();
658
659 doExit = true;
660 }
661 }
662
663 if (doExit)
664 exit(res2);
665 }
666
667 /* Legacy mode? */
668 res = machine_name_to_type(type, subtype,
669 &m->machine_type, &m->machine_subtype, &m->arch);
670 if (!res)
671 exit(1);
672 }
673
674 if (m->machine_type == MACHINE_NONE && msopts) {
675 fprintf(stderr, "Machine specific options used directly on "
676 "the command line, but no machine\nemulation specified?\n");
677 exit(1);
678 }
679
680
681 /* -i and -r are pretty verbose: */
682
683 if (m->instruction_trace && !verbose) {
684 fprintf(stderr, "Implicitly %sturning on -v, because"
685 " of -i\n", quiet_mode? "turning off -q and " : "");
686 verbose = 1;
687 quiet_mode = 0;
688 }
689
690 if (m->register_dump && !verbose) {
691 fprintf(stderr, "Implicitly %sturning on -v, because"
692 " of -r\n", quiet_mode? "turning off -q and " : "");
693 verbose = 1;
694 quiet_mode = 0;
695 }
696
697
698 /*
699 * Usually, an executable filename must be supplied.
700 *
701 * However, it is possible to boot directly from a harddisk image
702 * file. If no kernel is supplied, but a diskimage is being used,
703 * then try to boot from disk.
704 */
705 if (extra_argc == 0) {
706 if (using_switch_d) {
707 /* Booting directly from a disk image... */
708 } else {
709 usage(0);
710 fprintf(stderr, "\nNo filename given. Aborting.\n");
711 exit(1);
712 }
713 } else if (m->boot_kernel_filename[0] == '\0') {
714 /*
715 * Default boot_kernel_filename is "", which can be overriden
716 * by the -j command line option. If it is still "" here,
717 * and we're not booting directly from a disk image, then
718 * try to set it to the last part of the last file name
719 * given on the command line. (Last part = the stuff after
720 * the last slash.)
721 */
722 char *s = extra_argv[extra_argc - 1];
723 char *s2;
724
725 s2 = strrchr(s, '/');
726 if (s2 == NULL)
727 s2 = s;
728 else
729 s2 ++;
730
731 CHECK_ALLOCATION(m->boot_kernel_filename = strdup(s2));
732 }
733
734 if (m->n_gfx_cards < 0 || m->n_gfx_cards > 3) {
735 fprintf(stderr, "Bad number of gfx cards (-Z).\n");
736 exit(1);
737 }
738
739 if (!using_switch_Z && !m->x11_md.in_use)
740 m->n_gfx_cards = 0;
741
742 return 0;
743 }
744
745
746 /*
747 * main():
748 *
749 * Two kinds of emulations are started from here:
750 *
751 * o) Simple emulations, using command line arguments, compatible with
752 * earlier version of GXemul/mips64emul.
753 *
754 * o) Emulations set up by parsing special config files. (0 or more.)
755 */
main(int argc,char * argv[])756 int main(int argc, char *argv[])
757 {
758 /* Setting constants: */
759 int constant_yes = 1;
760 int constant_true = 1;
761 int constant_no = 0;
762 int constant_false = 0;
763
764 struct emul *emul;
765 int config_file = 0;
766
767 char **diskimages = NULL;
768 int n_diskimages = 0;
769 int i;
770
771
772 progname = argv[0];
773
774
775 /*
776 * Create the settings object, and add global settings to it:
777 *
778 * Read-only "constants": yes, no, true, false.
779 * Global emulator settings: verbose, single_step, ...
780 */
781 global_settings = settings_new();
782
783 settings_add(global_settings, "yes", 0, SETTINGS_TYPE_INT,
784 SETTINGS_FORMAT_YESNO, (void *)&constant_yes);
785 settings_add(global_settings, "no", 0, SETTINGS_TYPE_INT,
786 SETTINGS_FORMAT_YESNO, (void *)&constant_no);
787 settings_add(global_settings, "true", 0, SETTINGS_TYPE_INT,
788 SETTINGS_FORMAT_BOOL, (void *)&constant_true);
789 settings_add(global_settings, "false", 0, SETTINGS_TYPE_INT,
790 SETTINGS_FORMAT_BOOL, (void *)&constant_false);
791
792 /* Read-only settings: */
793 settings_add(global_settings, "single_step", 0,
794 SETTINGS_TYPE_INT, SETTINGS_FORMAT_YESNO, (void *)&single_step);
795
796 /* Read/write settings: */
797 settings_add(global_settings, "force_debugger_at_exit", 1,
798 SETTINGS_TYPE_INT, SETTINGS_FORMAT_YESNO,
799 (void *)&force_debugger_at_exit);
800 settings_add(global_settings, "verbose", 1,
801 SETTINGS_TYPE_INT, SETTINGS_FORMAT_YESNO, (void *)&verbose);
802 settings_add(global_settings, "quiet_mode", 1,
803 SETTINGS_TYPE_INT, SETTINGS_FORMAT_YESNO, (void *)&quiet_mode);
804
805 /* Initialize all emulator subsystems: */
806 console_init();
807 cpu_init();
808 device_init();
809 machine_init();
810 timer_init();
811
812 /* Create a simple emulation setup: */
813 emul = emul_new(NULL);
814 settings_add(global_settings, "emul", 1,
815 SETTINGS_TYPE_SUBSETTINGS, 0, emul->settings);
816
817 get_cmd_args(argc, argv, emul, &diskimages, &n_diskimages);
818
819 if (!skip_srandom_call) {
820 struct timeval tv;
821 gettimeofday(&tv, NULL);
822 srandom(tv.tv_sec ^ getpid() ^ tv.tv_usec);
823 }
824
825 /* Print startup message: */
826 debug("GXemul " VERSION" " COPYRIGHT_MSG"\n" SECONDARY_MSG
827 "Read the source code and/or documentation for other Copyright "
828 "messages.\n\n");
829
830 /* Simple initialization, from command line arguments: */
831 if (emul->machines[0]->machine_type != MACHINE_NONE) {
832 for (i=0; i<n_diskimages; i++)
833 diskimage_add(emul->machines[0], diskimages[i]);
834
835 /* Make sure that there are no configuration files as well: */
836 for (i=1; i<argc; i++)
837 if (argv[i][0] == '@') {
838 fprintf(stderr, "You can either start one "
839 "emulation with one machine directly from "
840 "the command\nline, or start one or more "
841 "emulations using configuration files."
842 " Not both.\n");
843 exit(1);
844 }
845
846 /* Initialize one emul: */
847 emul_simple_init(emul);
848 }
849
850 /* Initialize an emulation from a config file: */
851 for (i=1; i<argc; i++) {
852 if (argv[i][0] == '@') {
853 char *s = argv[i] + 1;
854
855 if (config_file) {
856 fprintf(stderr, "More than one configuration "
857 "file cannot be used.\n");
858 exit(1);
859 }
860
861 if (strlen(s) == 0 && i+1 < argc && *argv[i+1] != '@')
862 s = argv[++i];
863
864 /* Always allow slave xterms: */
865 console_allow_slaves(1);
866
867 /* Destroy the temporary emul, since it will
868 be overwritten: */
869 if (emul != NULL) {
870 emul_destroy(emul);
871 settings_remove(global_settings, "emul");
872 emul = NULL;
873 }
874
875 emul = emul_create_from_configfile(s);
876
877 settings_add(global_settings, "emul", 1,
878 SETTINGS_TYPE_SUBSETTINGS, 0, emul->settings);
879
880 config_file = 1;
881 }
882 }
883
884 if (emul->n_machines == 0) {
885 fprintf(stderr, "No emulations defined. Maybe you forgot to "
886 "use -E xx and/or -e yy, to specify\nthe machine type."
887 " For example:\n\n %s -e 3max -d disk.img\n\n"
888 "to boot an emulated DECstation 5000/200 with a disk "
889 "image.\n", progname);
890 exit(1);
891 }
892
893 if (emul->machines[0]->machine_type == MACHINE_NONE) {
894 printf("No machine type specified? Run gxemul -H for a list\n"
895 "of available machine types. Use the -e or -E option(s)\n"
896 "to specify the machine type.\n");
897 exit(1);
898 }
899
900 device_set_exit_on_error(0);
901 console_warn_if_slaves_are_needed(1);
902
903
904 /* Run the emulation: */
905 emul_run(emul);
906
907
908 /*
909 * Deinitialize everything:
910 */
911
912 console_deinit();
913
914 emul_destroy(emul);
915
916 settings_remove_all(global_settings);
917 settings_destroy(global_settings);
918
919 return 0;
920 }
921
922