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 * Machine registry.
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 "cpu.h"
39 #include "device.h"
40 #include "diskimage.h"
41 #include "emul.h"
42 #include "machine.h"
43 #include "memory.h"
44 #include "misc.h"
45 #include "settings.h"
46 #include "symbol.h"
47
48
49 /* This is initialized by machine_init(): */
50 struct machine_entry *first_machine_entry = NULL;
51
52
53 /*
54 * machine_new():
55 *
56 * Returns a reasonably initialized struct machine.
57 */
machine_new(char * name,struct emul * emul,int id)58 struct machine *machine_new(char *name, struct emul *emul, int id)
59 {
60 struct machine *m;
61
62 CHECK_ALLOCATION(m = (struct machine *) malloc(sizeof(struct machine)));
63 memset(m, 0, sizeof(struct machine));
64
65 /* Pointer back to the emul object that this machine belongs to: */
66 m->emul = emul;
67
68 if (name != NULL)
69 CHECK_ALLOCATION(m->name = strdup(name));
70
71 /* Full path, e.g. "machine[0]": */
72 CHECK_ALLOCATION(m->path = (char *) malloc(20));
73 snprintf(m->path, 20, "machine[%i]", id);
74
75 /* Sane default values: */
76 m->serial_nr = 1;
77 m->machine_type = MACHINE_NONE;
78 m->machine_subtype = MACHINE_NONE;
79 m->arch_pagesize = 4096; /* Should be overriden in
80 emul.c for other pagesizes. */
81 m->prom_emulation = 1;
82 m->allow_instruction_combinations = 1;
83 m->byte_order_override = NO_BYTE_ORDER_OVERRIDE;
84 m->boot_kernel_filename = strdup("");
85 m->boot_string_argument = NULL;
86 m->x11_md.scaledown = 1;
87 m->x11_md.scaleup = 1;
88 m->n_gfx_cards = 1;
89 symbol_init(&m->symbol_context);
90
91 /* Settings: */
92 m->settings = settings_new();
93 settings_add(m->settings, "name", 0,
94 SETTINGS_TYPE_STRING, SETTINGS_FORMAT_STRING,
95 (void *) &m->name);
96 settings_add(m->settings, "serial_nr", 0,
97 SETTINGS_TYPE_INT, SETTINGS_FORMAT_DECIMAL,
98 (void *) &m->serial_nr);
99 settings_add(m->settings, "arch_pagesize", 0,
100 SETTINGS_TYPE_INT, SETTINGS_FORMAT_DECIMAL,
101 (void *) &m->arch_pagesize);
102 settings_add(m->settings, "prom_emulation", 0,
103 SETTINGS_TYPE_INT, SETTINGS_FORMAT_YESNO,
104 (void *) &m->prom_emulation);
105 settings_add(m->settings, "allow_instruction_combinations", 0,
106 SETTINGS_TYPE_INT, SETTINGS_FORMAT_YESNO,
107 (void *) &m->allow_instruction_combinations);
108 settings_add(m->settings, "n_gfx_cards", 0,
109 SETTINGS_TYPE_INT, SETTINGS_FORMAT_DECIMAL,
110 (void *) &m->n_gfx_cards);
111 settings_add(m->settings, "statistics_enabled", 1,
112 SETTINGS_TYPE_INT, SETTINGS_FORMAT_YESNO,
113 (void *) &m->statistics.enabled);
114
115 return m;
116 }
117
118
119 /*
120 * machine_destroy():
121 *
122 * Destroys a machine object.
123 */
machine_destroy(struct machine * machine)124 void machine_destroy(struct machine *machine)
125 {
126 int i;
127
128 for (i=0; i<machine->ncpus; i++)
129 cpu_destroy(machine->cpus[i]);
130
131 // TODO: Memory leak; but it's ok, since the whole legacy thing should
132 // be replaced anyway.
133 // if (machine->name != NULL)
134 // free(machine->name);
135
136 if (machine->path != NULL)
137 free(machine->path);
138
139 /* Remove any remaining level-1 settings: */
140 settings_remove_all(machine->settings);
141 settings_destroy(machine->settings);
142
143 free(machine);
144 }
145
146
147 /*
148 * machine_name_to_type():
149 *
150 * Take a type and a subtype as strings, and convert them into numeric
151 * values used internally throughout the code.
152 *
153 * Return value is 1 on success, 0 if there was no match.
154 * Also, any errors/warnings are printed using fatal()/debug().
155 */
machine_name_to_type(char * stype,char * ssubtype,int * type,int * subtype,int * arch)156 int machine_name_to_type(char *stype, char *ssubtype,
157 int *type, int *subtype, int *arch)
158 {
159 struct machine_entry *me;
160 int i, j, k, nmatches = 0;
161
162 *type = MACHINE_NONE;
163 *subtype = 0;
164
165 /* Check stype, and optionally ssubtype: */
166 me = first_machine_entry;
167 while (me != NULL) {
168 for (i=0; i<me->n_aliases; i++)
169 if (strcasecmp(me->aliases[i], stype) == 0) {
170 /* Found a type: */
171 *type = me->machine_type;
172 *arch = me->arch;
173
174 if (me->n_subtypes == 0)
175 return 1;
176
177 /* Check for subtype: */
178 for (j=0; j<me->n_subtypes; j++)
179 for (k=0; k<me->subtype[j]->n_aliases;
180 k++)
181 if (strcasecmp(ssubtype,
182 me->subtype[j]->aliases[k]
183 ) == 0) {
184 *subtype = me->subtype[
185 j]->machine_subtype;
186 return 1;
187 }
188
189 fatal("Unknown subtype '%s' for emulation"
190 " '%s'\n", ssubtype, stype);
191 if (!ssubtype[0])
192 fatal("(Maybe you forgot the -e"
193 " command line option?)\n");
194 exit(1);
195 }
196
197 me = me->next;
198 }
199
200 /* Not found? Then just check ssubtype: */
201 me = first_machine_entry;
202 while (me != NULL) {
203 if (me->n_subtypes == 0) {
204 me = me->next;
205 continue;
206 }
207
208 /* Check for subtype: */
209 for (j=0; j<me->n_subtypes; j++)
210 for (k=0; k<me->subtype[j]->n_aliases; k++)
211 if (strcasecmp(ssubtype, me->subtype[j]->
212 aliases[k]) == 0) {
213 *type = me->machine_type;
214 *arch = me->arch;
215 *subtype = me->subtype[j]->
216 machine_subtype;
217 nmatches ++;
218 }
219
220 me = me->next;
221 }
222
223 switch (nmatches) {
224 case 0: fatal("\nSorry, emulation \"%s\"", stype);
225 if (ssubtype != NULL && ssubtype[0] != '\0')
226 fatal(" (subtype \"%s\")", ssubtype);
227 fatal(" is unknown.\n");
228 break;
229 case 1: return 1;
230 default:fatal("\nSorry, multiple matches for \"%s\"", stype);
231 if (ssubtype != NULL && ssubtype[0] != '\0')
232 fatal(" (subtype \"%s\")", ssubtype);
233 fatal(".\n");
234 }
235
236 *type = MACHINE_NONE;
237 *subtype = 0;
238
239 fatal("Use the -H command line option to get a list of "
240 "available types and subtypes.\n\n");
241
242 return 0;
243 }
244
245
246 /*
247 * machine_add_breakpoint_string():
248 *
249 * Add a breakpoint string to the machine. Later (in emul.c) these will be
250 * converted to actual breakpoints.
251 */
machine_add_breakpoint_string(struct machine * machine,char * str)252 void machine_add_breakpoint_string(struct machine *machine, char *str)
253 {
254 int n = machine->breakpoints.n + 1;
255
256 CHECK_ALLOCATION(machine->breakpoints.string = (char **)
257 realloc(machine->breakpoints.string, n * sizeof(char *)));
258 CHECK_ALLOCATION(machine->breakpoints.addr = (uint64_t *)
259 realloc(machine->breakpoints.addr, n * sizeof(uint64_t)));
260 CHECK_ALLOCATION(machine->breakpoints.string[machine->breakpoints.n] =
261 strdup(optarg));
262
263 machine->breakpoints.addr[machine->breakpoints.n] = 0;
264 machine->breakpoints.n ++;
265 }
266
267
268 /*
269 * machine_add_tickfunction():
270 *
271 * Adds a tick function (a function called every now and then, depending on
272 * clock cycle count) to a machine.
273 *
274 * If tickshift is non-zero, a tick will occur every (1 << tickshift) cycles.
275 * This is used for the normal (fast dyntrans) emulation modes.
276 *
277 * If tickshift is zero, then this is a cycle-accurate tick function.
278 * The hz value is used in this case.
279 */
machine_add_tickfunction(struct machine * machine,void (* func)(struct cpu *,void *),void * extra,int tickshift)280 void machine_add_tickfunction(struct machine *machine, void (*func)
281 (struct cpu *, void *), void *extra, int tickshift)
282 {
283 int n = machine->tick_functions.n_entries;
284
285 CHECK_ALLOCATION(machine->tick_functions.ticks_till_next = (int *) realloc(
286 machine->tick_functions.ticks_till_next, (n+1) * sizeof(int)));
287 CHECK_ALLOCATION(machine->tick_functions.ticks_reset_value = (int *) realloc(
288 machine->tick_functions.ticks_reset_value, (n+1) * sizeof(int)));
289 CHECK_ALLOCATION(machine->tick_functions.f = (void (**)(cpu*,void*)) realloc(
290 machine->tick_functions.f, (n+1) * sizeof(void *)));
291 CHECK_ALLOCATION(machine->tick_functions.extra = (void **) realloc(
292 machine->tick_functions.extra, (n+1) * sizeof(void *)));
293
294 /*
295 * The dyntrans subsystem wants to run code in relatively
296 * large chunks without checking for external interrupts;
297 * too low tickshifts are not allowed.
298 */
299 if (tickshift < N_SAFE_DYNTRANS_LIMIT_SHIFT) {
300 fatal("ERROR! tickshift = %i, less than "
301 "N_SAFE_DYNTRANS_LIMIT_SHIFT (%i)\n",
302 tickshift, N_SAFE_DYNTRANS_LIMIT_SHIFT);
303 exit(1);
304 }
305
306 machine->tick_functions.ticks_till_next[n] = 0;
307 machine->tick_functions.ticks_reset_value[n] = 1 << tickshift;
308 machine->tick_functions.f[n] = func;
309 machine->tick_functions.extra[n] = extra;
310
311 machine->tick_functions.n_entries = n + 1;
312 }
313
314
315 /*
316 * machine_statistics_init():
317 *
318 * Initialize the parts of a machine struct that deal with instruction
319 * statistics gathering.
320 *
321 * Note: The fname argument contains "flags:filename".
322 */
machine_statistics_init(struct machine * machine,char * fname)323 void machine_statistics_init(struct machine *machine, char *fname)
324 {
325 int n_fields = 0;
326 char *pcolon = fname;
327 const char *mode = "a"; /* Append by default */
328
329 machine->allow_instruction_combinations = 0;
330
331 if (machine->statistics.fields != NULL) {
332 fprintf(stderr, "Only one -s option is allowed.\n");
333 exit(1);
334 }
335
336 machine->statistics.enabled = 1;
337 CHECK_ALLOCATION(machine->statistics.fields = (char *) malloc(1));
338 machine->statistics.fields[0] = '\0';
339
340 while (*pcolon && *pcolon != ':')
341 pcolon ++;
342
343 if (*pcolon != ':') {
344 fprintf(stderr, "The syntax for the -s option is: "
345 "-s flags:filename\nYou omitted the flags. Run g"
346 "xemul -h for a list of available flags.\n");
347 exit(1);
348 }
349
350 while (*fname != ':') {
351 switch (*fname) {
352
353 /* Type flags: */
354 case 'v':
355 case 'i':
356 case 'p':
357 CHECK_ALLOCATION(machine->statistics.fields = (char *) realloc(
358 machine->statistics.fields, strlen(
359 machine->statistics.fields) + 2));
360 machine->statistics.fields[n_fields ++] = *fname;
361 machine->statistics.fields[n_fields] = '\0';
362 break;
363
364 /* Optional flags: */
365 case 'o':
366 mode = "w";
367 break;
368 case 'd':
369 machine->statistics.enabled = 0;
370 break;
371
372 default:fprintf(stderr, "Unknown flag '%c' used with the"
373 " -s option. Aborting.\n", *fname);
374 exit(1);
375 }
376 fname ++;
377 }
378
379 fname ++; /* point to the filename after the colon */
380
381 CHECK_ALLOCATION(machine->statistics.filename = strdup(fname));
382 machine->statistics.file = fopen(machine->statistics.filename, mode);
383 }
384
385
386 /*
387 * machine_dumpinfo():
388 *
389 * Dumps info about a machine in some kind of readable format. (Used by
390 * the 'machine' debugger command.)
391 */
machine_dumpinfo(struct machine * m)392 void machine_dumpinfo(struct machine *m)
393 {
394 int i;
395
396 debug("serial nr: %i", m->serial_nr);
397 if (m->nr_of_nics > 0)
398 debug(" (nr of NICs: %i)", m->nr_of_nics);
399 debug("\n");
400
401 debug("memory: %i MB", m->physical_ram_in_mb);
402 if (m->memory_offset_in_mb != 0)
403 debug(" (offset by %i MB)", m->memory_offset_in_mb);
404 if (m->random_mem_contents)
405 debug(", randomized contents");
406 debug("\n");
407
408 if (!m->prom_emulation)
409 debug("PROM emulation disabled\n");
410
411 for (i=0; i<m->ncpus; i++)
412 cpu_dumpinfo(m, m->cpus[i]);
413
414 if (m->ncpus > 1)
415 debug("Bootstrap cpu is nr %i\n", m->bootstrap_cpu);
416
417 if (m->slow_serial_interrupts_hack_for_linux)
418 debug("Using slow_serial_interrupts_hack_for_linux\n");
419
420 if (m->x11_md.in_use) {
421 debug("Using X11");
422 if (m->x11_md.scaledown > 1)
423 debug(", scaledown %i", m->x11_md.scaledown);
424 if (m->x11_md.scaleup > 1)
425 debug(", scaleup %i", m->x11_md.scaleup);
426 if (m->x11_md.n_display_names > 0) {
427 for (i=0; i<m->x11_md.n_display_names; i++) {
428 debug(i? ", " : " (");
429 debug("\"%s\"", m->x11_md.display_names[i]);
430 }
431 debug(")");
432 }
433 debug("\n");
434 }
435
436 diskimage_dump_info(m);
437
438 if (m->force_netboot)
439 debug("Forced netboot\n");
440 }
441
442
443 /*
444 * machine_setup():
445 *
446 * This function initializes memory, registers, and/or devices
447 * required by specific machine emulations.
448 */
machine_setup(struct machine * machine)449 void machine_setup(struct machine *machine)
450 {
451 struct machine_entry *me;
452 struct cpu *cpu = machine->cpus[machine->bootstrap_cpu];
453
454 machine->bootdev_id = diskimage_bootdev(machine, &machine->bootdev_type);
455 machine->machine_name = NULL;
456
457 /* TODO: Move this somewhere else? */
458 if (machine->boot_string_argument == NULL) {
459 switch (machine->machine_type) {
460 case MACHINE_ARC:
461 machine->boot_string_argument = strdup("-aN");
462 break;
463 case MACHINE_CATS:
464 machine->boot_string_argument = strdup("-A");
465 break;
466 case MACHINE_PMAX:
467 machine->boot_string_argument = strdup("-a");
468 break;
469 default:
470 /* Important, because boot_string_argument should
471 not be set to NULL: */
472 machine->boot_string_argument = strdup("");
473 }
474 }
475
476
477 /*
478 * If the machine has a setup function in src/machines/machine_*.c
479 * then use that one, otherwise use the old hardcoded stuff here:
480 */
481
482 me = first_machine_entry;
483 while (me != NULL) {
484 if (machine->machine_type == me->machine_type &&
485 me->setup != NULL) {
486 me->setup(machine, cpu);
487 break;
488 }
489 me = me->next;
490 }
491
492 if (me == NULL) {
493 fatal("Unknown emulation type %i\n", machine->machine_type);
494 exit(1);
495 }
496
497 if (machine->machine_name != NULL)
498 debug("machine: %s", machine->machine_name);
499
500 if (machine->emulated_hz > 0)
501 debug(" (%.2f MHz)", (float)machine->emulated_hz / 1000000);
502 debug("\n");
503
504 if (machine->bootstr != NULL) {
505 debug("bootstring%s: %s", (machine->bootarg!=NULL &&
506 strlen(machine->bootarg) >= 1)? "(+bootarg)" : "",
507 machine->bootstr);
508 if (machine->bootarg != NULL && strlen(machine->bootarg) >= 1)
509 debug(" %s", machine->bootarg);
510 debug("\n");
511 }
512 }
513
514
515 /*
516 * machine_memsize_fix():
517 *
518 * Sets physical_ram_in_mb (if not already set), and memory_offset_in_mb,
519 * depending on machine type.
520 */
machine_memsize_fix(struct machine * m)521 void machine_memsize_fix(struct machine *m)
522 {
523 if (m == NULL) {
524 fatal("machine_defaultmemsize(): m == NULL?\n");
525 exit(1);
526 }
527
528 if (m->physical_ram_in_mb == 0) {
529 struct machine_entry *me = first_machine_entry;
530 while (me != NULL) {
531 if (m->machine_type == me->machine_type &&
532 me->set_default_ram != NULL) {
533 me->set_default_ram(m);
534 break;
535 }
536 me = me->next;
537 }
538 }
539
540 /* Special SGI memory offsets: (TODO: move this somewhere else) */
541 if (m->machine_type == MACHINE_SGI) {
542 switch (m->machine_subtype) {
543 case 20:
544 case 22:
545 case 24:
546 case 26:
547 m->memory_offset_in_mb = 128;
548 break;
549 case 28:
550 case 30:
551 m->memory_offset_in_mb = 512;
552 break;
553 }
554 }
555
556 if (m->physical_ram_in_mb == 0)
557 m->physical_ram_in_mb = DEFAULT_RAM_IN_MB;
558 }
559
560
561 /*
562 * machine_default_cputype():
563 *
564 * Sets m->cpu_name, if it isn't already set, depending on the machine type.
565 */
machine_default_cputype(struct machine * m)566 void machine_default_cputype(struct machine *m)
567 {
568 struct machine_entry *me;
569
570 if (m == NULL) {
571 fatal("machine_default_cputype(): m == NULL?\n");
572 exit(1);
573 }
574
575 /* Already set? Then return. */
576 if (m->cpu_name != NULL)
577 return;
578
579 me = first_machine_entry;
580 while (me != NULL) {
581 if (m->machine_type == me->machine_type &&
582 me->set_default_cpu != NULL) {
583 me->set_default_cpu(m);
584 break;
585 }
586 me = me->next;
587 }
588
589 if (m->cpu_name == NULL) {
590 fprintf(stderr, "machine_default_cputype(): no default"
591 " cpu for machine type %i subtype %i\n",
592 m->machine_type, m->machine_subtype);
593 exit(1);
594 }
595 }
596
597
598 /*****************************************************************************/
599
600
601 /*
602 * machine_run():
603 *
604 * Run one or more instructions on all CPUs in this machine. (Usually,
605 * around N_SAFE_DYNTRANS_LIMIT instructions will be run by the dyntrans
606 * system.)
607 *
608 * Return value is 1 if any CPU in this machine is still running,
609 * or 0 if all CPUs are stopped.
610 */
machine_run(struct machine * machine)611 int machine_run(struct machine *machine)
612 {
613 struct cpu **cpus = machine->cpus;
614 int ncpus = machine->ncpus, cpu0instrs = 0, i, te;
615
616 for (i=0; i<ncpus; i++) {
617 if (cpus[i]->running) {
618 int instrs_run = cpus[i]->run_instr(cpus[i]);
619 if (i == 0)
620 cpu0instrs += instrs_run;
621 }
622 }
623
624 /*
625 * Hardware 'ticks': (clocks, interrupt sources...)
626 *
627 * Here, cpu0instrs is the number of instructions executed on cpu0.
628 *
629 * TODO: This should be redesigned into some "mainbus" stuff instead!
630 */
631
632 for (te=0; te<machine->tick_functions.n_entries; te++) {
633 machine->tick_functions.ticks_till_next[te] -= cpu0instrs;
634 if (machine->tick_functions.ticks_till_next[te] <= 0) {
635 while (machine->tick_functions.ticks_till_next[te]<=0) {
636 machine->tick_functions.ticks_till_next[te] +=
637 machine->tick_functions.
638 ticks_reset_value[te];
639 }
640
641 machine->tick_functions.f[te](cpus[0],
642 machine->tick_functions.extra[te]);
643 }
644 }
645
646 /* Is any CPU still alive? */
647 for (i=0; i<ncpus; i++)
648 if (cpus[i]->running)
649 return 1;
650
651 return 0;
652 }
653
654
655 /*****************************************************************************/
656
657
658 /*
659 * machine_entry_new():
660 *
661 * This function creates a new machine_entry struct, and fills it with some
662 * valid data; it is up to the caller to add additional data that weren't
663 * passed as arguments to this function, such as alias names and machine
664 * subtypes.
665 */
machine_entry_new(const char * name,int arch,int oldstyle_type)666 struct machine_entry *machine_entry_new(const char *name, int arch,
667 int oldstyle_type)
668 {
669 struct machine_entry *me;
670
671 CHECK_ALLOCATION(me = (struct machine_entry *) malloc(sizeof(struct machine_entry)));
672 memset(me, 0, sizeof(struct machine_entry));
673
674 me->name = name;
675 me->arch = arch;
676 me->machine_type = oldstyle_type;
677 me->n_aliases = 0;
678 me->aliases = NULL;
679 me->n_subtypes = 0;
680 me->setup = NULL;
681
682 return me;
683 }
684
685
686 /*
687 * machine_entry_add_alias():
688 *
689 * This function adds an "alias" to a machine entry.
690 */
machine_entry_add_alias(struct machine_entry * me,const char * name)691 void machine_entry_add_alias(struct machine_entry *me, const char *name)
692 {
693 me->n_aliases ++;
694
695 CHECK_ALLOCATION(me->aliases = (char **) realloc(me->aliases,
696 sizeof(char *) * me->n_aliases));
697
698 me->aliases[me->n_aliases - 1] = strdup(name);
699 }
700
701
702 /*
703 * machine_entry_add_subtype():
704 *
705 * This function adds a subtype to a machine entry. The argument list after
706 * oldstyle_subtype is a list of one or more char *, followed by NULL. E.g.:
707 *
708 * machine_entry_add_subtype(me, "Machine X", MACHINE_X,
709 * "machine-x", "x", NULL);
710 */
machine_entry_add_subtype(struct machine_entry * me,const char * name,int oldstyle_subtype,...)711 void machine_entry_add_subtype(struct machine_entry *me, const char *name,
712 int oldstyle_subtype, ...)
713 {
714 va_list argp;
715 struct machine_entry_subtype *mes;
716
717 /* Allocate a new subtype struct: */
718 CHECK_ALLOCATION(mes = (struct machine_entry_subtype *)
719 malloc(sizeof(struct machine_entry_subtype)));
720
721 /* Add the subtype to the machine entry: */
722 me->n_subtypes ++;
723 CHECK_ALLOCATION(me->subtype = (struct
724 machine_entry_subtype **) realloc(me->subtype, sizeof(struct
725 machine_entry_subtype *) * me->n_subtypes));
726 me->subtype[me->n_subtypes - 1] = mes;
727
728 /* Fill the struct with subtype data: */
729 memset(mes, 0, sizeof(struct machine_entry_subtype));
730 mes->name = name;
731 mes->machine_subtype = oldstyle_subtype;
732
733 /* ... and all aliases: */
734 mes->n_aliases = 0;
735 mes->aliases = NULL;
736
737 va_start(argp, oldstyle_subtype);
738
739 for (;;) {
740 char *s = va_arg(argp, char *);
741 if (s == NULL)
742 break;
743
744 mes->n_aliases ++;
745 CHECK_ALLOCATION(mes->aliases = (char **)
746 realloc(mes->aliases, sizeof(char *) * mes->n_aliases));
747
748 mes->aliases[mes->n_aliases - 1] = s;
749 }
750
751 va_end(argp);
752 }
753
754
755 /*
756 * machine_entry_register():
757 *
758 * Inserts a new machine_entry into the machine entries list.
759 */
machine_entry_register(struct machine_entry * me,int arch)760 void machine_entry_register(struct machine_entry *me, int arch)
761 {
762 struct machine_entry *prev, *next;
763
764 /* Only insert it if the architecture is implemented in this
765 emulator configuration: */
766 if (cpu_family_ptr_by_number(arch) == NULL)
767 return;
768
769 prev = NULL;
770 next = first_machine_entry;
771
772 for (;;) {
773 if (next == NULL)
774 break;
775 if (strcasecmp(me->name, next->name) < 0)
776 break;
777
778 prev = next;
779 next = next->next;
780 }
781
782 if (prev != NULL)
783 prev->next = me;
784 else
785 first_machine_entry = me;
786 me->next = next;
787 }
788
789
790 /*
791 * machine_list_available_types_and_cpus():
792 *
793 * List all available machine types (for example when running the emulator
794 * with just -H as command line argument).
795 */
machine_list_available_types_and_cpus(void)796 void machine_list_available_types_and_cpus(void)
797 {
798 struct machine_entry *me;
799 int iadd = DEBUG_INDENTATION * 2;
800
801 debug("Available CPU types:\n\n");
802
803 debug_indentation(iadd);
804 cpu_list_available_types();
805 debug_indentation(-iadd);
806
807 debug("\nMost of the CPU types are bogus, and not really implemented."
808 " The main effect of\nselecting a specific CPU type is to choose "
809 "what kind of 'id' it will have.\n\nAvailable machine types (with "
810 "aliases) and their subtypes:\n\n");
811
812 debug_indentation(iadd);
813 me = first_machine_entry;
814
815 if (me == NULL)
816 fatal("No machines defined!\n");
817
818 while (me != NULL) {
819 int i, j, iadd2 = DEBUG_INDENTATION;
820
821 debug("%s [%s] (", me->name,
822 cpu_family_ptr_by_number(me->arch)->name);
823 for (i=0; i<me->n_aliases; i++)
824 debug("%s\"%s\"", i? ", " : "", me->aliases[i]);
825 debug(")\n");
826
827 debug_indentation(iadd2);
828 for (i=0; i<me->n_subtypes; i++) {
829 struct machine_entry_subtype *mes;
830 mes = me->subtype[i];
831 debug("- %s", mes->name);
832 debug(" (");
833 for (j=0; j<mes->n_aliases; j++)
834 debug("%s\"%s\"", j? ", " : "",
835 mes->aliases[j]);
836 debug(")\n");
837 }
838 debug_indentation(-iadd2);
839
840 me = me->next;
841 }
842 debug_indentation(-iadd);
843
844 debug("\nMost of the machine types are bogus too. Please read the "
845 "GXemul documentation\nfor information about which machine types "
846 "that actually work. Use the alias\nwhen selecting a machine type "
847 "or subtype, not the real name.\n");
848
849 debug("\n");
850 }
851
852
853 /*
854 * machine_init():
855 *
856 * This function should be called before any other machine_*() function
857 * is used. automachine_init() registers all machines in src/machines/.
858 */
machine_init(void)859 void machine_init(void)
860 {
861 if (first_machine_entry != NULL) {
862 fatal("machine_init(): already called?\n");
863 exit(1);
864 }
865
866 automachine_init();
867 }
868
869