25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: emul.c,v 1.284 2007/04/19 15:18:15 debug Exp $ |
* $Id: emul.c,v 1.302 2007/08/29 20:36:49 debug Exp $ |
29 |
* |
* |
30 |
* Emulation startup and misc. routines. |
* Emulation startup and misc. routines. |
31 |
*/ |
*/ |
52 |
#include "misc.h" |
#include "misc.h" |
53 |
#include "net.h" |
#include "net.h" |
54 |
#include "settings.h" |
#include "settings.h" |
|
#include "sgi_arcbios.h" |
|
55 |
#include "timer.h" |
#include "timer.h" |
56 |
|
#include "useremul.h" |
57 |
#include "x11.h" |
#include "x11.h" |
58 |
|
|
59 |
|
|
69 |
extern int old_quiet_mode; |
extern int old_quiet_mode; |
70 |
extern int quiet_mode; |
extern int quiet_mode; |
71 |
|
|
|
extern struct emul *debugger_emul; |
|
|
extern struct diskimage *diskimages[]; |
|
|
|
|
|
|
|
|
static void print_separator(void) |
|
|
{ |
|
|
int i = 79; |
|
|
while (i-- > 0) |
|
|
debug("-"); |
|
|
debug("\n"); |
|
|
} |
|
|
|
|
72 |
|
|
73 |
/* |
/* |
74 |
* add_dump_points(): |
* add_breakpoints(): |
75 |
* |
* |
76 |
* Take the strings breakpoint_string[] and convert to addresses |
* Take the strings breakpoint_string[] and convert to addresses |
77 |
* (and store them in breakpoint_addr[]). |
* (and store them in breakpoint_addr[]). |
78 |
* |
* |
79 |
* TODO: This function should be moved elsewhere. |
* TODO: This function should be moved elsewhere. |
80 |
*/ |
*/ |
81 |
static void add_dump_points(struct machine *m) |
static void add_breakpoints(struct machine *m) |
82 |
{ |
{ |
83 |
int i; |
int i; |
84 |
int string_flag; |
int string_flag; |
85 |
uint64_t dp; |
uint64_t dp; |
86 |
|
|
87 |
for (i=0; i<m->n_breakpoints; i++) { |
for (i=0; i<m->breakpoints.n; i++) { |
88 |
string_flag = 0; |
string_flag = 0; |
89 |
dp = strtoull(m->breakpoint_string[i], NULL, 0); |
dp = strtoull(m->breakpoints.string[i], NULL, 0); |
90 |
|
|
91 |
/* |
/* |
92 |
* If conversion resulted in 0, then perhaps it is a |
* If conversion resulted in 0, then perhaps it is a |
95 |
if (dp == 0) { |
if (dp == 0) { |
96 |
uint64_t addr; |
uint64_t addr; |
97 |
int res = get_symbol_addr(&m->symbol_context, |
int res = get_symbol_addr(&m->symbol_context, |
98 |
m->breakpoint_string[i], &addr); |
m->breakpoints.string[i], &addr); |
99 |
if (!res) { |
if (!res) { |
100 |
fprintf(stderr, |
fprintf(stderr, |
101 |
"ERROR! Breakpoint '%s' could not be" |
"ERROR! Breakpoint '%s' could not be" |
102 |
" parsed\n", |
" parsed\n", |
103 |
m->breakpoint_string[i]); |
m->breakpoints.string[i]); |
104 |
|
exit(1); |
105 |
} else { |
} else { |
106 |
dp = addr; |
dp = addr; |
107 |
string_flag = 1; |
string_flag = 1; |
118 |
dp |= 0xffffffff00000000ULL; |
dp |= 0xffffffff00000000ULL; |
119 |
} |
} |
120 |
|
|
121 |
m->breakpoint_addr[i] = dp; |
m->breakpoints.addr[i] = dp; |
122 |
|
|
123 |
debug("breakpoint %i: 0x%llx", i, (long long)dp); |
debug("breakpoint %i: 0x%"PRIx64, i, dp); |
124 |
if (string_flag) |
if (string_flag) |
125 |
debug(" (%s)", m->breakpoint_string[i]); |
debug(" (%s)", m->breakpoints.string[i]); |
126 |
debug("\n"); |
debug("\n"); |
127 |
} |
} |
128 |
} |
} |
142 |
* |
* |
143 |
* Returns a reasonably initialized struct emul. |
* Returns a reasonably initialized struct emul. |
144 |
*/ |
*/ |
145 |
struct emul *emul_new(char *name, int id) |
struct emul *emul_new(char *name) |
146 |
{ |
{ |
147 |
struct emul *e; |
struct emul *e; |
|
e = malloc(sizeof(struct emul)); |
|
|
if (e == NULL) { |
|
|
fprintf(stderr, "out of memory in emul_new()\n"); |
|
|
exit(1); |
|
|
} |
|
148 |
|
|
149 |
|
CHECK_ALLOCATION(e = malloc(sizeof(struct emul))); |
150 |
memset(e, 0, sizeof(struct emul)); |
memset(e, 0, sizeof(struct emul)); |
151 |
|
|
|
e->path = malloc(15); |
|
|
if (e->path == NULL) { |
|
|
fprintf(stderr, "out of memory\n"); |
|
|
exit(1); |
|
|
} |
|
|
snprintf(e->path, 15, "emul[%i]", id); |
|
|
|
|
152 |
e->settings = settings_new(); |
e->settings = settings_new(); |
153 |
|
|
154 |
settings_add(e->settings, "n_machines", 0, |
settings_add(e->settings, "n_machines", 0, |
162 |
e->next_serial_nr = 1; |
e->next_serial_nr = 1; |
163 |
|
|
164 |
if (name != NULL) { |
if (name != NULL) { |
165 |
e->name = strdup(name); |
CHECK_ALLOCATION(e->name = strdup(name)); |
|
if (e->name == NULL) { |
|
|
fprintf(stderr, "out of memory in emul_new()\n"); |
|
|
exit(1); |
|
|
} |
|
|
|
|
166 |
settings_add(e->settings, "name", 0, |
settings_add(e->settings, "name", 0, |
167 |
SETTINGS_TYPE_STRING, SETTINGS_FORMAT_STRING, |
SETTINGS_TYPE_STRING, SETTINGS_FORMAT_STRING, |
168 |
(void *) &e->name); |
(void *) &e->name); |
217 |
m = machine_new(name, e, e->n_machines); |
m = machine_new(name, e, e->n_machines); |
218 |
m->serial_nr = (e->next_serial_nr ++); |
m->serial_nr = (e->next_serial_nr ++); |
219 |
|
|
220 |
i = e->n_machines; |
i = e->n_machines ++; |
221 |
|
|
222 |
e->n_machines ++; |
CHECK_ALLOCATION(e->machines = realloc(e->machines, |
223 |
e->machines = realloc(e->machines, |
sizeof(struct machine *) * e->n_machines)); |
|
sizeof(struct machine *) * e->n_machines); |
|
|
if (e->machines == NULL) { |
|
|
fprintf(stderr, "emul_add_machine(): out of memory\n"); |
|
|
exit(1); |
|
|
} |
|
224 |
|
|
225 |
e->machines[i] = m; |
e->machines[i] = m; |
226 |
|
|
361 |
uint64_t memory_amount, entrypoint = 0, gp = 0, toc = 0; |
uint64_t memory_amount, entrypoint = 0, gp = 0, toc = 0; |
362 |
int byte_order; |
int byte_order; |
363 |
|
|
364 |
debug("machine \"%s\":\n", m->name); |
if (m->name != NULL) |
365 |
|
debug("machine \"%s\":\n", m->name); |
366 |
|
else |
367 |
|
debug("machine:\n"); |
368 |
|
|
369 |
debug_indentation(iadd); |
debug_indentation(iadd); |
370 |
|
|
371 |
/* For userland-only, this decides which ARCH/cpu_name to use: */ |
/* For userland-only, this decides which ARCH/cpu_name to use: */ |
419 |
/* TODO: This should be moved elsewhere... */ |
/* TODO: This should be moved elsewhere... */ |
420 |
if (m->machine_type == MACHINE_BEBOX) |
if (m->machine_type == MACHINE_BEBOX) |
421 |
m->ncpus = 2; |
m->ncpus = 2; |
|
else if (m->machine_type == MACHINE_ARC && |
|
|
m->machine_subtype == MACHINE_ARC_NEC_R96) |
|
|
m->ncpus = 2; |
|
|
else if (m->machine_type == MACHINE_ARC && |
|
|
m->machine_subtype == MACHINE_ARC_NEC_R98) |
|
|
m->ncpus = 4; |
|
422 |
else |
else |
423 |
m->ncpus = 1; |
m->ncpus = 1; |
424 |
} |
} |
425 |
m->cpus = malloc(sizeof(struct cpu *) * m->ncpus); |
|
426 |
if (m->cpus == NULL) { |
CHECK_ALLOCATION(m->cpus = malloc(sizeof(struct cpu *) * m->ncpus)); |
|
fprintf(stderr, "out of memory\n"); |
|
|
exit(1); |
|
|
} |
|
427 |
memset(m->cpus, 0, sizeof(struct cpu *) * m->ncpus); |
memset(m->cpus, 0, sizeof(struct cpu *) * m->ncpus); |
428 |
|
|
429 |
debug("cpu0"); |
debug("cpu0"); |
440 |
} |
} |
441 |
debug("\n"); |
debug("\n"); |
442 |
|
|
|
#if 0 |
|
|
/* Special case: The Playstation Portable has an additional CPU: */ |
|
|
if (m->machine_type == MACHINE_PSP) { |
|
|
debug("cpu%i: ", m->ncpus); |
|
|
m->cpus[m->ncpus] = cpu_new(m->memory, m, |
|
|
0 /* use 0 here to show info with debug() */, |
|
|
"Allegrex" /* TODO */); |
|
|
debug("\n"); |
|
|
m->ncpus ++; |
|
|
} |
|
|
#endif |
|
|
|
|
443 |
if (m->use_random_bootstrap_cpu) |
if (m->use_random_bootstrap_cpu) |
444 |
m->bootstrap_cpu = random() % m->ncpus; |
m->bootstrap_cpu = random() % m->ncpus; |
445 |
else |
else |
453 |
m->userland_emul, NULL, NULL, NULL); |
m->userland_emul, NULL, NULL, NULL); |
454 |
|
|
455 |
switch (m->arch) { |
switch (m->arch) { |
456 |
#ifdef ENABLE_ALPHA |
|
457 |
case ARCH_ALPHA: |
case ARCH_ALPHA: |
458 |
cpu->memory_rw = alpha_userland_memory_rw; |
cpu->memory_rw = alpha_userland_memory_rw; |
459 |
break; |
break; |
460 |
#endif |
|
461 |
default:cpu->memory_rw = userland_memory_rw; |
default: |
462 |
|
cpu->memory_rw = userland_memory_rw; |
463 |
} |
} |
464 |
} |
} |
465 |
|
|
466 |
if (m->use_x11) |
if (m->x11_md.in_use) |
467 |
x11_init(m); |
x11_init(m); |
468 |
|
|
469 |
/* Fill memory with random bytes: */ |
/* Fill memory with random bytes: */ |
532 |
fread(buf, 1, sizeof(buf), tmp_f); |
fread(buf, 1, sizeof(buf), tmp_f); |
533 |
if (buf[0]==0x1f && buf[1]==0x8b) { |
if (buf[0]==0x1f && buf[1]==0x8b) { |
534 |
size_t zzlen = strlen(name_to_load)*2 + 100; |
size_t zzlen = strlen(name_to_load)*2 + 100; |
535 |
char *zz = malloc(zzlen); |
char *zz; |
536 |
|
|
537 |
|
CHECK_ALLOCATION(zz = malloc(zzlen)); |
538 |
debug("gunziping %s\n", name_to_load); |
debug("gunziping %s\n", name_to_load); |
539 |
|
|
540 |
/* |
/* |
541 |
* gzip header found. If this was a file |
* gzip header found. If this was a file |
542 |
* extracted from, say, a CDROM image, then it |
* extracted from, say, a CDROM image, then it |
553 |
} else { |
} else { |
554 |
/* gunzip into new temp file: */ |
/* gunzip into new temp file: */ |
555 |
int tmpfile_handle; |
int tmpfile_handle; |
556 |
char *new_temp_name = |
char *new_temp_name; |
557 |
strdup("/tmp/gxemul.XXXXXXXXXXXX"); |
char *tmpdir = getenv("TMPDIR"); |
558 |
|
|
559 |
|
if (tmpdir == NULL) |
560 |
|
tmpdir = DEFAULT_TMP_DIR; |
561 |
|
|
562 |
|
CHECK_ALLOCATION(new_temp_name = |
563 |
|
malloc(300)); |
564 |
|
snprintf(new_temp_name, 300, |
565 |
|
"%s/gxemul.XXXXXXXXXXXX", tmpdir); |
566 |
|
|
567 |
tmpfile_handle = mkstemp(new_temp_name); |
tmpfile_handle = mkstemp(new_temp_name); |
568 |
close(tmpfile_handle); |
close(tmpfile_handle); |
569 |
snprintf(zz, zzlen, "gunzip -c '%s' > " |
snprintf(zz, zzlen, "gunzip -c '%s' > " |
577 |
fclose(tmp_f); |
fclose(tmp_f); |
578 |
} |
} |
579 |
|
|
|
/* |
|
|
* Ugly (but usable) hack for Playstation Portable: If the |
|
|
* filename ends with ".pbp" and the file contains an ELF |
|
|
* header, then extract the ELF file into a temporary file. |
|
|
*/ |
|
|
if (strlen(name_to_load) > 4 && strcasecmp(name_to_load + |
|
|
strlen(name_to_load) - 4, ".pbp") == 0 && |
|
|
(tmp_f = fopen(name_to_load, "r")) != NULL) { |
|
|
off_t filesize, j, found=0; |
|
|
unsigned char *buf; |
|
|
fseek(tmp_f, 0, SEEK_END); |
|
|
filesize = ftello(tmp_f); |
|
|
fseek(tmp_f, 0, SEEK_SET); |
|
|
buf = malloc(filesize); |
|
|
if (buf == NULL) { |
|
|
fprintf(stderr, "out of memory while trying" |
|
|
" to read %s\n", name_to_load); |
|
|
exit(1); |
|
|
} |
|
|
fread(buf, 1, filesize, tmp_f); |
|
|
fclose(tmp_f); |
|
|
/* Search for the ELF header, from offset 1 (!): */ |
|
|
for (j=1; j<filesize - 4; j++) |
|
|
if (memcmp(buf + j, ELFMAG, SELFMAG) == 0) { |
|
|
found = j; |
|
|
break; |
|
|
} |
|
|
if (found != 0) { |
|
|
int tmpfile_handle; |
|
|
char *new_temp_name = |
|
|
strdup("/tmp/gxemul.XXXXXXXXXXXX"); |
|
|
debug("extracting ELF from %s (offset 0x%x)\n", |
|
|
name_to_load, (int)found); |
|
|
tmpfile_handle = mkstemp(new_temp_name); |
|
|
write(tmpfile_handle, buf + found, |
|
|
filesize - found); |
|
|
close(tmpfile_handle); |
|
|
name_to_load = new_temp_name; |
|
|
remove_after_load = 1; |
|
|
} |
|
|
} |
|
|
|
|
580 |
byte_order = NO_BYTE_ORDER_OVERRIDE; |
byte_order = NO_BYTE_ORDER_OVERRIDE; |
581 |
|
|
582 |
/* |
/* |
610 |
cpu->pc &= 0xfffffffc; |
cpu->pc &= 0xfffffffc; |
611 |
break; |
break; |
612 |
|
|
613 |
case ARCH_AVR: |
case ARCH_M32R: |
614 |
cpu->pc &= 0xfffff; |
if (cpu->pc & 3) { |
615 |
if (cpu->pc & 1) { |
fatal("M32R: lowest bits of pc set: TODO\n"); |
|
fatal("AVR: lowest bit of pc set: TODO\n"); |
|
616 |
exit(1); |
exit(1); |
617 |
} |
} |
618 |
|
cpu->pc &= 0xfffffffc; |
619 |
break; |
break; |
620 |
|
|
621 |
case ARCH_M88K: |
case ARCH_M88K: |
622 |
|
if (cpu->pc & 3) { |
623 |
|
fatal("M88K: lowest bits of pc set: TODO\n"); |
624 |
|
exit(1); |
625 |
|
} |
626 |
|
cpu->pc &= 0xfffffffc; |
627 |
break; |
break; |
628 |
|
|
629 |
case ARCH_MIPS: |
case ARCH_MIPS: |
699 |
m->cpus[i]->running = 0; |
m->cpus[i]->running = 0; |
700 |
} |
} |
701 |
|
|
702 |
/* Add PC dump points: */ |
/* Parse and add breakpoints: */ |
703 |
add_dump_points(m); |
add_breakpoints(m); |
704 |
|
|
705 |
/* TODO: This is MIPS-specific! */ |
/* TODO: This is MIPS-specific! */ |
706 |
if (m->machine_type == MACHINE_PMAX && |
if (m->machine_type == MACHINE_PMAX && |
715 |
m->machine_type == MACHINE_SGI) && m->prom_emulation) |
m->machine_type == MACHINE_SGI) && m->prom_emulation) |
716 |
add_arc_components(m); |
add_arc_components(m); |
717 |
|
|
718 |
debug("starting cpu%i at ", m->bootstrap_cpu); |
debug("cpu%i: starting at ", m->bootstrap_cpu); |
|
switch (m->arch) { |
|
|
|
|
|
case ARCH_ARM: |
|
|
/* ARM cpus aren't 64-bit: */ |
|
|
debug("0x%08"PRIx32, (uint32_t) entrypoint); |
|
|
break; |
|
719 |
|
|
720 |
case ARCH_AVR: |
switch (m->arch) { |
|
/* Atmel AVR uses a 16-bit or 22-bit program counter: */ |
|
|
debug("0x%04x", (int) entrypoint); |
|
|
break; |
|
721 |
|
|
722 |
case ARCH_MIPS: |
case ARCH_MIPS: |
723 |
if (cpu->is_32bit) { |
if (cpu->is_32bit) { |
736 |
} |
} |
737 |
break; |
break; |
738 |
|
|
|
case ARCH_PPC: |
|
|
if (cpu->cd.ppc.bits == 32) |
|
|
debug("0x%08"PRIx32, (uint32_t) entrypoint); |
|
|
else |
|
|
debug("0x%016"PRIx64, (uint64_t) entrypoint); |
|
|
break; |
|
|
|
|
739 |
default: |
default: |
740 |
if (cpu->is_32bit) |
if (cpu->is_32bit) |
741 |
debug("0x%08"PRIx32, (uint32_t) cpu->pc); |
debug("0x%08"PRIx32, (uint32_t) cpu->pc); |
755 |
*/ |
*/ |
756 |
void emul_dumpinfo(struct emul *e) |
void emul_dumpinfo(struct emul *e) |
757 |
{ |
{ |
758 |
int j, nm, iadd = DEBUG_INDENTATION; |
int i; |
759 |
|
|
760 |
if (e->net != NULL) |
if (e->net != NULL) |
761 |
net_dumpinfo(e->net); |
net_dumpinfo(e->net); |
762 |
|
|
763 |
nm = e->n_machines; |
for (i = 0; i < e->n_machines; i++) { |
764 |
for (j=0; j<nm; j++) { |
if (e->n_machines > 1) |
765 |
debug("machine %i: \"%s\"\n", j, e->machines[j]->name); |
debug("machine %i: \"%s\"\n", i, e->machines[i]->name); |
766 |
debug_indentation(iadd); |
else |
767 |
machine_dumpinfo(e->machines[j]); |
debug("machine:\n"); |
768 |
debug_indentation(-iadd); |
|
769 |
|
debug_indentation(DEBUG_INDENTATION); |
770 |
|
|
771 |
|
machine_dumpinfo(e->machines[i]); |
772 |
|
|
773 |
|
debug_indentation(-DEBUG_INDENTATION); |
774 |
} |
} |
775 |
} |
} |
776 |
|
|
826 |
* |
* |
827 |
* Create an emul struct by reading settings from a configuration file. |
* Create an emul struct by reading settings from a configuration file. |
828 |
*/ |
*/ |
829 |
struct emul *emul_create_from_configfile(char *fname, int id) |
struct emul *emul_create_from_configfile(char *fname) |
830 |
{ |
{ |
831 |
int iadd = DEBUG_INDENTATION; |
int iadd = DEBUG_INDENTATION; |
832 |
struct emul *e = emul_new(fname, id); |
struct emul *e = emul_new(fname); |
833 |
|
|
834 |
debug("Creating emulation from configfile \"%s\":\n", fname); |
debug("Creating emulation from configfile \"%s\":\n", fname); |
835 |
debug_indentation(iadd); |
debug_indentation(iadd); |
844 |
/* |
/* |
845 |
* emul_run(): |
* emul_run(): |
846 |
* |
* |
847 |
* o) Set up things needed before running emulations. |
* o) Set up things needed before running an emulation. |
848 |
* |
* |
849 |
* o) Run emulations (one or more, in parallel). |
* o) Run instructions in all machines. |
850 |
* |
* |
851 |
* o) De-initialize things. |
* o) De-initialize things. |
852 |
*/ |
*/ |
853 |
void emul_run(struct emul **emuls, int n_emuls) |
void emul_run(struct emul *emul) |
854 |
{ |
{ |
|
struct emul *e; |
|
855 |
int i = 0, j, go = 1, n, anything; |
int i = 0, j, go = 1, n, anything; |
856 |
|
|
|
if (n_emuls < 1) { |
|
|
fprintf(stderr, "emul_run(): no thing to do\n"); |
|
|
return; |
|
|
} |
|
|
|
|
857 |
atexit(fix_console); |
atexit(fix_console); |
858 |
|
|
859 |
/* Initialize the interactive debugger: */ |
/* Initialize the interactive debugger: */ |
860 |
debugger_init(emuls, n_emuls); |
debugger_init(emul); |
861 |
|
|
862 |
/* Run any additional debugger commands before starting: */ |
/* Run any additional debugger commands before starting: */ |
863 |
for (i=0; i<n_emuls; i++) { |
if (emul->n_debugger_cmds > 0) { |
864 |
struct emul *emul = emuls[i]; |
int j; |
865 |
if (emul->n_debugger_cmds > 0) { |
if (i == 0) |
866 |
int j; |
print_separator_line(); |
867 |
if (i == 0) |
for (j = 0; j < emul->n_debugger_cmds; j ++) { |
868 |
print_separator(); |
debug("> %s\n", emul->debugger_cmds[j]); |
869 |
for (j = 0; j < emul->n_debugger_cmds; j ++) { |
debugger_execute_cmd(emul->debugger_cmds[j], |
870 |
debug("> %s\n", emul->debugger_cmds[j]); |
strlen(emul->debugger_cmds[j])); |
|
debugger_execute_cmd(emul->debugger_cmds[j], |
|
|
strlen(emul->debugger_cmds[j])); |
|
|
} |
|
871 |
} |
} |
872 |
} |
} |
873 |
|
|
874 |
print_separator(); |
print_separator_line(); |
875 |
debug("\n"); |
debug("\n"); |
876 |
|
|
877 |
|
|
885 |
* (or sends SIGSTOP) and then continues. It makes sure that the |
* (or sends SIGSTOP) and then continues. It makes sure that the |
886 |
* terminal is in an expected state. |
* terminal is in an expected state. |
887 |
*/ |
*/ |
888 |
console_init_main(emuls[0]); /* TODO: what is a good argument? */ |
console_init_main(emul); |
889 |
|
|
890 |
signal(SIGINT, debugger_activate); |
signal(SIGINT, debugger_activate); |
891 |
signal(SIGCONT, console_sigcont); |
signal(SIGCONT, console_sigcont); |
892 |
|
|
894 |
if (!verbose) |
if (!verbose) |
895 |
quiet_mode = 1; |
quiet_mode = 1; |
896 |
|
|
897 |
/* Initialize all CPUs in all machines in all emulations: */ |
|
898 |
for (i=0; i<n_emuls; i++) { |
/* Initialize all CPUs in all machines: */ |
899 |
e = emuls[i]; |
for (j=0; j<emul->n_machines; j++) |
900 |
if (e == NULL) |
cpu_run_init(emul->machines[j]); |
|
continue; |
|
|
for (j=0; j<e->n_machines; j++) |
|
|
cpu_run_init(e->machines[j]); |
|
|
} |
|
901 |
|
|
902 |
/* TODO: Generalize: */ |
/* TODO: Generalize: */ |
903 |
if (emuls[0]->machines[0]->show_trace_tree) |
if (emul->machines[0]->show_trace_tree) |
904 |
cpu_functioncall_trace(emuls[0]->machines[0]->cpus[0], |
cpu_functioncall_trace(emul->machines[0]->cpus[0], |
905 |
emuls[0]->machines[0]->cpus[0]->pc); |
emul->machines[0]->cpus[0]->pc); |
906 |
|
|
907 |
/* Start emulated clocks: */ |
/* Start emulated clocks: */ |
908 |
timer_start(); |
timer_start(); |
909 |
|
|
910 |
|
|
911 |
/* |
/* |
912 |
* MAIN LOOP: |
* MAIN LOOP: |
913 |
* |
* |
914 |
* Run all emulations in parallel, running each machine in |
* Run all emulations in parallel, running instructions from each |
915 |
* each emulation. |
* cpu in each machine. |
916 |
*/ |
*/ |
917 |
while (go) { |
while (go) { |
918 |
|
struct cpu *bootcpu = emul->machines[0]->cpus[ |
919 |
|
emul->machines[0]->bootstrap_cpu]; |
920 |
|
|
921 |
go = 0; |
go = 0; |
922 |
|
|
923 |
/* Flush X11 and serial console output every now and then: */ |
/* Flush X11 and serial console output every now and then: */ |
924 |
if (emuls[0]->machines[0]->ninstrs > |
if (bootcpu->ninstrs > bootcpu->ninstrs_flush + (1<<19)) { |
925 |
emuls[0]->machines[0]->ninstrs_flush + (1<<19)) { |
x11_check_event(emul); |
|
x11_check_event(emuls, n_emuls); |
|
926 |
console_flush(); |
console_flush(); |
927 |
emuls[0]->machines[0]->ninstrs_flush = |
bootcpu->ninstrs_flush = bootcpu->ninstrs; |
|
emuls[0]->machines[0]->ninstrs; |
|
928 |
} |
} |
929 |
|
|
930 |
if (emuls[0]->machines[0]->ninstrs > |
if (bootcpu->ninstrs > bootcpu->ninstrs_show + (1<<25)) { |
931 |
emuls[0]->machines[0]->ninstrs_show + (1<<25)) { |
bootcpu->ninstrs_since_gettimeofday += |
932 |
emuls[0]->machines[0]->ninstrs_since_gettimeofday += |
(bootcpu->ninstrs - bootcpu->ninstrs_show); |
933 |
(emuls[0]->machines[0]->ninstrs - |
cpu_show_cycles(emul->machines[0], 0); |
934 |
emuls[0]->machines[0]->ninstrs_show); |
bootcpu->ninstrs_show = bootcpu->ninstrs; |
|
cpu_show_cycles(emuls[0]->machines[0], 0); |
|
|
emuls[0]->machines[0]->ninstrs_show = |
|
|
emuls[0]->machines[0]->ninstrs; |
|
935 |
} |
} |
936 |
|
|
937 |
if (single_step == ENTER_SINGLE_STEPPING) { |
if (single_step == ENTER_SINGLE_STEPPING) { |
938 |
/* TODO: Cleanup! */ |
/* TODO: Cleanup! */ |
939 |
old_instruction_trace = |
old_instruction_trace = |
940 |
emuls[0]->machines[0]->instruction_trace; |
emul->machines[0]->instruction_trace; |
941 |
old_quiet_mode = quiet_mode; |
old_quiet_mode = quiet_mode; |
942 |
old_show_trace_tree = |
old_show_trace_tree = |
943 |
emuls[0]->machines[0]->show_trace_tree; |
emul->machines[0]->show_trace_tree; |
944 |
emuls[0]->machines[0]->instruction_trace = 1; |
emul->machines[0]->instruction_trace = 1; |
945 |
emuls[0]->machines[0]->show_trace_tree = 1; |
emul->machines[0]->show_trace_tree = 1; |
946 |
quiet_mode = 0; |
quiet_mode = 0; |
947 |
single_step = SINGLE_STEPPING; |
single_step = SINGLE_STEPPING; |
948 |
} |
} |
950 |
if (single_step == SINGLE_STEPPING) |
if (single_step == SINGLE_STEPPING) |
951 |
debugger(); |
debugger(); |
952 |
|
|
953 |
for (i=0; i<n_emuls; i++) { |
for (j=0; j<emul->n_machines; j++) { |
954 |
e = emuls[i]; |
anything = machine_run(emul->machines[j]); |
955 |
|
if (anything) |
956 |
for (j=0; j<e->n_machines; j++) { |
go = 1; |
|
anything = machine_run(e->machines[j]); |
|
|
if (anything) |
|
|
go = 1; |
|
|
} |
|
957 |
} |
} |
958 |
} |
} |
959 |
|
|
960 |
/* Stop any running timers: */ |
/* Stop any running timers: */ |
961 |
timer_stop(); |
timer_stop(); |
962 |
|
|
963 |
/* Deinitialize all CPUs in all machines in all emulations: */ |
/* Deinitialize all CPUs in all machines: */ |
964 |
for (i=0; i<n_emuls; i++) { |
for (j=0; j<emul->n_machines; j++) |
965 |
e = emuls[i]; |
cpu_run_deinit(emul->machines[j]); |
|
if (e == NULL) |
|
|
continue; |
|
|
for (j=0; j<e->n_machines; j++) |
|
|
cpu_run_deinit(e->machines[j]); |
|
|
} |
|
966 |
|
|
967 |
/* force_debugger_at_exit flag set? Then enter the debugger: */ |
/* force_debugger_at_exit flag set? Then enter the debugger: */ |
968 |
if (force_debugger_at_exit) { |
if (force_debugger_at_exit) { |
973 |
|
|
974 |
/* Any machine using X11? Then wait before exiting: */ |
/* Any machine using X11? Then wait before exiting: */ |
975 |
n = 0; |
n = 0; |
976 |
for (i=0; i<n_emuls; i++) |
for (j=0; j<emul->n_machines; j++) |
977 |
for (j=0; j<emuls[i]->n_machines; j++) |
if (emul->machines[j]->x11_md.in_use) |
978 |
if (emuls[i]->machines[j]->use_x11) |
n++; |
979 |
n++; |
|
980 |
if (n > 0) { |
if (n > 0) { |
981 |
printf("Press enter to quit.\n"); |
printf("Press enter to quit.\n"); |
982 |
while (!console_charavail(MAIN_CONSOLE)) { |
while (!console_charavail(MAIN_CONSOLE)) { |
983 |
x11_check_event(emuls, n_emuls); |
x11_check_event(emul); |
984 |
usleep(10000); |
usleep(10000); |
985 |
} |
} |
986 |
console_readchar(MAIN_CONSOLE); |
console_readchar(MAIN_CONSOLE); |