/[gxemul]/trunk/src/cpu.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/src/cpu.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4 - (show annotations)
Mon Oct 8 16:18:00 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 14050 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.707 2005/04/27 16:37:33 debug Exp $
20050408	Some minor updates to the wdc. Linux now doesn't complain
		anymore if a disk is non-present.
20050409	Various minor fixes (a bintrans bug, and some other things).
		The wdc seems to work with Playstation2 emulation, but there
		is a _long_ annoying delay when disks are detected.
		Fixing a really important bintrans bug (when devices and RAM
		are mixed within 4KB pages), which was triggered with
		NetBSD/playstation2 kernels.
20050410	Adding a dummy dev_ps2_ether (just so that NetBSD doesn't
		complain as much during bootup).
		Symbols starting with '$' are now ignored.
		Renaming dev_ps2_ohci.c to dev_ohci.c, etc.
20050411	Moving the bintrans-cache-isolation check from cpu_mips.c to
		cpu_mips_coproc.c. (I thought this would give a speedup, but
		it's not noticable.)
		Better playstation2 sbus interrupt code.
		Skip ahead many ticks if the count register is read manually.
		(This increases the speed of delay-loops that simply read
		the count register.)
20050412	Updates to the playstation2 timer/interrupt code.
		Some other minor updates.
20050413	NetBSD/cobalt runs from a disk image :-) including userland;
		updating the documentation on how to install NetBSD/cobalt
		using NetBSD/pmax (!).
		Some minor bintrans updates (no real speed improvement) and
		other minor updates (playstation2 now uses the -o options).
20050414	Adding a dummy x86 (and AMD64) mode.
20050415	Adding some (32-bit and 16-bit) x86 instructions.
		Adding some initial support for non-SCSI, non-IDE floppy
		images. (The x86 mode can boot from these, more or less.)
		Moving the devices/ and include/ directories to src/devices/
		and src/include/, respectively.
20050416	Continuing on the x86 stuff. (Adding pc_bios.c and some simple
		support for software interrupts in 16-bit mode.)
20050417	Ripping out most of the x86 instruction decoding stuff, trying
		to rewrite it in a cleaner way.
		Disabling some of the least working CPU families in the
		configure script (sparc, x86, alpha, hppa), so that they are
		not enabled by default.
20050418	Trying to fix the bug which caused problems when turning on
		and off bintrans interactively, by flushing the bintrans cache
		whenever bintrans is manually (re)enabled.
20050419	Adding the 'lswi' ppc instruction.
		Minor updates to the x86 instruction decoding.
20050420	Renaming x86 register name indices from R_xx to X86_R_xx (this
		makes building on Tru64 nicer).
20050422	Adding a check for duplicate MIPS TLB entries on tlbwr/tlbwi.
20050427	Adding screenshots to guestoses.html.
		Some minor fixes and testing for the next release.

==============  RELEASE 0.3.2  ==============


1 /*
2 * Copyright (C) 2005 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: cpu.c,v 1.292 2005/04/14 21:01:53 debug Exp $
29 *
30 * Common routines for CPU emulation. (Not specific to any CPU type.)
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <string.h>
37
38 #include "cpu.h"
39 #include "machine.h"
40 #include "misc.h"
41
42
43 extern int quiet_mode;
44 extern int show_opcode_statistics;
45
46
47 static struct cpu_family *first_cpu_family = NULL;
48
49
50 /*
51 * cpu_new():
52 *
53 * Create a new cpu object. Each family is tried in sequence until a
54 * CPU family recognizes the cpu_type_name.
55 */
56 struct cpu *cpu_new(struct memory *mem, struct machine *machine,
57 int cpu_id, char *name)
58 {
59 struct cpu *c;
60 struct cpu_family *fp;
61 char *cpu_type_name;
62
63 if (name == NULL) {
64 fprintf(stderr, "cpu_new(): cpu name = NULL?\n");
65 exit(1);
66 }
67
68 cpu_type_name = strdup(name);
69 if (cpu_type_name == NULL) {
70 fprintf(stderr, "cpu_new(): out of memory\n");
71 exit(1);
72 }
73
74 fp = first_cpu_family;
75
76 while (fp != NULL) {
77 if (fp->cpu_new != NULL) {
78 c = fp->cpu_new(mem, machine, cpu_id, cpu_type_name);
79 if (c != NULL) {
80 /* Some sanity-checks: */
81 if (c->memory_rw == NULL) {
82 fatal("No memory_rw?\n");
83 exit(1);
84 }
85
86 return c;
87 }
88 }
89
90 fp = fp->next;
91 }
92
93 fprintf(stderr, "cpu_new(): unknown cpu type '%s'\n", cpu_type_name);
94 exit(1);
95 }
96
97
98 /*
99 * cpu_show_full_statistics():
100 *
101 * Show detailed statistics on opcode usage on each cpu.
102 */
103 void cpu_show_full_statistics(struct machine *m)
104 {
105 if (m->cpu_family == NULL ||
106 m->cpu_family->show_full_statistics == NULL)
107 fatal("cpu_show_full_statistics(): NULL\n");
108 else
109 m->cpu_family->show_full_statistics(m);
110 }
111
112
113 /*
114 * cpu_tlbdump():
115 *
116 * Called from the debugger to dump the TLB in a readable format.
117 * x is the cpu number to dump, or -1 to dump all CPUs.
118 *
119 * If rawflag is nonzero, then the TLB contents isn't formated nicely,
120 * just dumped.
121 */
122 void cpu_tlbdump(struct machine *m, int x, int rawflag)
123 {
124 if (m->cpu_family == NULL || m->cpu_family->tlbdump == NULL)
125 fatal("cpu_tlbdump(): NULL\n");
126 else
127 m->cpu_family->tlbdump(m, x, rawflag);
128 }
129
130
131 /*
132 * cpu_register_match():
133 *
134 * Used by the debugger.
135 */
136 void cpu_register_match(struct machine *m, char *name,
137 int writeflag, uint64_t *valuep, int *match_register)
138 {
139 if (m->cpu_family == NULL || m->cpu_family->register_match == NULL)
140 fatal("cpu_register_match(): NULL\n");
141 else
142 m->cpu_family->register_match(m, name, writeflag,
143 valuep, match_register);
144 }
145
146
147 /*
148 * cpu_disassemble_instr():
149 *
150 * Convert an instruction word into human readable format, for instruction
151 * tracing.
152 */
153 int cpu_disassemble_instr(struct machine *m, struct cpu *cpu,
154 unsigned char *instr, int running, uint64_t addr, int bintrans)
155 {
156 if (m->cpu_family == NULL || m->cpu_family->disassemble_instr == NULL) {
157 fatal("cpu_disassemble_instr(): NULL\n");
158 return 0;
159 } else
160 return m->cpu_family->disassemble_instr(cpu, instr,
161 running, addr, bintrans);
162 }
163
164
165 /*
166 * cpu_register_dump():
167 *
168 * Dump cpu registers in a relatively readable format.
169 *
170 * gprs: set to non-zero to dump GPRs. (CPU dependant.)
171 * coprocs: set bit 0..x to dump registers in coproc 0..x. (CPU dependant.)
172 */
173 void cpu_register_dump(struct machine *m, struct cpu *cpu,
174 int gprs, int coprocs)
175 {
176 if (m->cpu_family == NULL || m->cpu_family->register_dump == NULL)
177 fatal("cpu_register_dump(): NULL\n");
178 else
179 m->cpu_family->register_dump(cpu, gprs, coprocs);
180 }
181
182
183 /*
184 * cpu_interrupt():
185 *
186 * Assert an interrupt.
187 * Return value is 1 if the interrupt was asserted, 0 otherwise.
188 */
189 int cpu_interrupt(struct cpu *cpu, uint64_t irq_nr)
190 {
191 if (cpu->machine->cpu_family == NULL ||
192 cpu->machine->cpu_family->interrupt == NULL) {
193 fatal("cpu_interrupt(): NULL\n");
194 return 0;
195 } else
196 return cpu->machine->cpu_family->interrupt(cpu, irq_nr);
197 }
198
199
200 /*
201 * cpu_interrupt_ack():
202 *
203 * Acknowledge an interrupt.
204 * Return value is 1 if the interrupt was deasserted, 0 otherwise.
205 */
206 int cpu_interrupt_ack(struct cpu *cpu, uint64_t irq_nr)
207 {
208 if (cpu->machine->cpu_family == NULL ||
209 cpu->machine->cpu_family->interrupt_ack == NULL) {
210 /* debug("cpu_interrupt_ack(): NULL\n"); */
211 return 0;
212 } else
213 return cpu->machine->cpu_family->interrupt_ack(cpu, irq_nr);
214 }
215
216
217 /*
218 * cpu_run():
219 *
220 * Run instructions on all CPUs in this machine, for a "medium duration"
221 * (or until all CPUs have halted).
222 *
223 * Return value is 1 if anything happened, 0 if all CPUs are stopped.
224 */
225 int cpu_run(struct emul *emul, struct machine *m)
226 {
227 if (m->cpu_family == NULL || m->cpu_family->run == NULL) {
228 fatal("cpu_run(): NULL\n");
229 return 0;
230 } else
231 return m->cpu_family->run(emul, m);
232 }
233
234
235 /*
236 * cpu_dumpinfo():
237 *
238 * Dumps info about a CPU using debug(). "cpu0: CPUNAME, running" (or similar)
239 * is outputed, and it is up to CPU dependant code to complete the line.
240 */
241 void cpu_dumpinfo(struct machine *m, struct cpu *cpu)
242 {
243 debug("cpu%i: %s, %s", cpu->cpu_id, cpu->name,
244 cpu->running? "running" : "stopped");
245
246 if (m->cpu_family == NULL || m->cpu_family->dumpinfo == NULL)
247 fatal("cpu_dumpinfo(): NULL\n");
248 else
249 m->cpu_family->dumpinfo(cpu);
250 }
251
252
253 /*
254 * cpu_list_available_types():
255 *
256 * Print a list of available CPU types for each cpu family.
257 */
258 void cpu_list_available_types(void)
259 {
260 struct cpu_family *fp;
261 int iadd = 4;
262
263 fp = first_cpu_family;
264
265 if (fp == NULL) {
266 debug("No CPUs defined!\n");
267 return;
268 }
269
270 while (fp != NULL) {
271 debug("%s:\n", fp->name);
272 debug_indentation(iadd);
273 if (fp->list_available_types != NULL)
274 fp->list_available_types();
275 else
276 debug("(internal error: list_available_types"
277 " = NULL)\n");
278 debug_indentation(-iadd);
279
280 fp = fp->next;
281 }
282 }
283
284
285 /*
286 * cpu_run_deinit():
287 *
288 * Shuts down all CPUs in a machine when ending a simulation. (This function
289 * should only need to be called once for each machine.)
290 */
291 void cpu_run_deinit(struct emul *emul, struct machine *machine)
292 {
293 int te;
294
295 /*
296 * Two last ticks of every hardware device. This will allow
297 * framebuffers to draw the last updates to the screen before
298 * halting.
299 */
300 for (te=0; te<machine->n_tick_entries; te++) {
301 machine->tick_func[te](machine->cpus[0],
302 machine->tick_extra[te]);
303 machine->tick_func[te](machine->cpus[0],
304 machine->tick_extra[te]);
305 }
306
307 debug("cpu_run_deinit(): All CPUs halted.\n");
308
309 if (machine->show_nr_of_instructions || !quiet_mode)
310 cpu_show_cycles(machine, &machine->starttime,
311 machine->ncycles, 1);
312
313 if (show_opcode_statistics)
314 cpu_show_full_statistics(machine);
315
316 fflush(stdout);
317 }
318
319
320 /*
321 * cpu_show_cycles():
322 *
323 * If automatic adjustment of clock interrupts is turned on, then recalculate
324 * emulated_hz. Also, if show_nr_of_instructions is on, then print a
325 * line to stdout about how many instructions/cycles have been executed so
326 * far.
327 */
328 void cpu_show_cycles(struct machine *machine,
329 struct timeval *starttime, int64_t ncycles, int forced)
330 {
331 uint64_t offset, pc;
332 int is_32bit = 0, instrs_per_cycle;
333 char *symbol;
334 int64_t mseconds, ninstrs;
335 struct timeval tv;
336 int h, m, s, ms, d;
337
338 static int64_t mseconds_last = 0;
339 static int64_t ninstrs_last = -1;
340
341 if (machine->arch != ARCH_MIPS) {
342 fatal("cpu_show_cycles(): not yet for !MIPS\n");
343 return;
344 }
345
346 if (machine->cpus[machine->bootstrap_cpu]->cd.mips.cpu_type.isa_level
347 < 3 || machine->cpus[machine->bootstrap_cpu]->cd.mips.cpu_type.
348 isa_level == 32)
349 is_32bit = 1;
350 pc = machine->cpus[machine->bootstrap_cpu]->pc;
351 instrs_per_cycle = machine->cpus[machine->bootstrap_cpu]->
352 cd.mips.cpu_type.instrs_per_cycle;
353
354 gettimeofday(&tv, NULL);
355 mseconds = (tv.tv_sec - starttime->tv_sec) * 1000
356 + (tv.tv_usec - starttime->tv_usec) / 1000;
357
358 if (mseconds == 0)
359 mseconds = 1;
360
361 if (mseconds - mseconds_last == 0)
362 mseconds ++;
363
364 ninstrs = ncycles * instrs_per_cycle;
365
366 if (machine->automatic_clock_adjustment) {
367 static int first_adjustment = 1;
368
369 /* Current nr of cycles per second: */
370 int64_t cur_cycles_per_second = 1000 *
371 (ninstrs-ninstrs_last) / (mseconds-mseconds_last)
372 / instrs_per_cycle;
373
374 if (cur_cycles_per_second < 1000000)
375 cur_cycles_per_second = 1000000;
376
377 if (first_adjustment) {
378 machine->emulated_hz = cur_cycles_per_second;
379 first_adjustment = 0;
380 } else {
381 machine->emulated_hz = (15 * machine->emulated_hz +
382 cur_cycles_per_second) / 16;
383 }
384
385 debug("[ updating emulated_hz to %lli Hz ]\n",
386 (long long)machine->emulated_hz);
387 }
388
389
390 /* RETURN here, unless show_nr_of_instructions (-N) is turned on: */
391 if (!machine->show_nr_of_instructions && !forced)
392 goto do_return;
393
394
395 printf("[ ");
396
397 if (!machine->automatic_clock_adjustment) {
398 d = machine->emulated_hz / 1000;
399 if (d < 1)
400 d = 1;
401 ms = ncycles / d;
402 h = ms / 3600000;
403 ms -= 3600000 * h;
404 m = ms / 60000;
405 ms -= 60000 * m;
406 s = ms / 1000;
407 ms -= 1000 * s;
408
409 printf("emulated time = %02i:%02i:%02i.%03i; ", h, m, s, ms);
410 }
411
412 printf("cycles=%lli", (long long) ncycles);
413
414 if (instrs_per_cycle > 1)
415 printf(" (%lli instrs)", (long long) ninstrs);
416
417 /* Instructions per second, and average so far: */
418 printf("; i/s=%lli avg=%lli",
419 (long long) ((long long)1000 * (ninstrs-ninstrs_last)
420 / (mseconds-mseconds_last)),
421 (long long) ((long long)1000 * ninstrs / mseconds));
422
423 symbol = get_symbol_name(&machine->symbol_context, pc, &offset);
424
425 if (is_32bit)
426 printf("; pc=%08x", (int)pc);
427 else
428 printf("; pc=%016llx", (long long)pc);
429
430 printf(" <%s> ]\n", symbol? symbol : "no symbol");
431
432 do_return:
433 ninstrs_last = ninstrs;
434 mseconds_last = mseconds;
435 }
436
437
438 /*
439 * cpu_run_init():
440 *
441 * Prepare to run instructions on all CPUs in this machine. (This function
442 * should only need to be called once for each machine.)
443 */
444 void cpu_run_init(struct emul *emul, struct machine *machine)
445 {
446 int ncpus = machine->ncpus;
447 int te;
448
449 machine->a_few_cycles = 1048576;
450 machine->ncycles_flush = 0;
451 machine->ncycles = 0;
452 machine->ncycles_show = 0;
453
454 /*
455 * Instead of doing { one cycle, check hardware ticks }, we
456 * can do { n cycles, check hardware ticks }, as long as
457 * n is at most as much as the lowest number of cycles/tick
458 * for any hardware device.
459 */
460 for (te=0; te<machine->n_tick_entries; te++) {
461 if (machine->ticks_reset_value[te] < machine->a_few_cycles)
462 machine->a_few_cycles = machine->ticks_reset_value[te];
463 }
464
465 machine->a_few_cycles >>= 1;
466 if (machine->a_few_cycles < 1)
467 machine->a_few_cycles = 1;
468
469 if (ncpus > 1 && machine->max_random_cycles_per_chunk == 0)
470 machine->a_few_cycles = 1;
471
472 /* debug("cpu_run_init(): a_few_cycles = %i\n",
473 machine->a_few_cycles); */
474
475 /* For performance measurement: */
476 gettimeofday(&machine->starttime, NULL);
477 }
478
479
480 /*
481 * add_cpu_family():
482 *
483 * Allocates a cpu_family struct and calls an init function for the
484 * family to fill in reasonable data and pointers.
485 */
486 static void add_cpu_family(int (*family_init)(struct cpu_family *), int arch)
487 {
488 struct cpu_family *fp, *tmp;
489 int res;
490
491 fp = malloc(sizeof(struct cpu_family));
492 if (fp == NULL) {
493 fprintf(stderr, "add_cpu_family(): out of memory\n");
494 exit(1);
495 }
496 memset(fp, 0, sizeof(struct cpu_family));
497
498 /*
499 * family_init() returns 1 if the struct has been filled with
500 * valid data, 0 if suppor for the cpu family isn't compiled
501 * into the emulator.
502 */
503 res = family_init(fp);
504 if (!res) {
505 free(fp);
506 return;
507 }
508 fp->arch = arch;
509 fp->next = NULL;
510
511 /* Add last in family chain: */
512 tmp = first_cpu_family;
513 if (tmp == NULL) {
514 first_cpu_family = fp;
515 } else {
516 while (tmp->next != NULL)
517 tmp = tmp->next;
518 tmp->next = fp;
519 }
520 }
521
522
523 /*
524 * cpu_family_ptr_by_number():
525 *
526 * Returns a pointer to a CPU family based on the ARCH_* integers.
527 */
528 struct cpu_family *cpu_family_ptr_by_number(int arch)
529 {
530 struct cpu_family *fp;
531 fp = first_cpu_family;
532
533 /* YUCK! This is too hardcoded! TODO */
534
535 while (fp != NULL) {
536 if (arch == fp->arch)
537 return fp;
538 fp = fp->next;
539 }
540
541 return NULL;
542 }
543
544
545 /*
546 * cpu_init():
547 *
548 * Should be called before any other cpu_*() function.
549 */
550 void cpu_init(void)
551 {
552 /* Note: These are registered in alphabetic order. */
553 add_cpu_family(alpha_cpu_family_init, ARCH_ALPHA);
554 add_cpu_family(hppa_cpu_family_init, ARCH_HPPA);
555 add_cpu_family(mips_cpu_family_init, ARCH_MIPS);
556 add_cpu_family(ppc_cpu_family_init, ARCH_PPC);
557 add_cpu_family(sparc_cpu_family_init, ARCH_SPARC);
558 add_cpu_family(urisc_cpu_family_init, ARCH_URISC);
559 add_cpu_family(x86_cpu_family_init, ARCH_X86);
560 }
561

  ViewVC Help
Powered by ViewVC 1.1.26