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