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