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