/[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

Annotation of /trunk/src/cpu.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6 - (hide annotations)
Mon Oct 8 16:18:11 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 14090 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.772 2005/06/04 12:02:16 debug Exp $
20050428	Disabling the "-fmove-all-movables" option in the configure
		script, because it causes the compile to fail on OpenBSD/sgi.
20050502	Minor updates.
20050503	Removing the WRT54G mode (it was bogus anyway), and adding a
		comment about Windows NT for MIPS in doc/experiments.html.
		Minor updates to the x86 instruction decoding.
20050504	Adding some more x86 instructions.
		Adding support for reading files from ISO9660 CDROMs (including
		gzipped files). It's an ugly hack, but it seems to work.
		Various other minor updates (dev_vga.c, pc_bios.c etc).
20050505	Some more x86-related updates.
		Beginning (what I hope will be) a major code cleanup phase.
		"bootris" (an x86 bootsector) runs :-)
20050506	Adding some more x86 instructions.
20050507	tmpnam => mkstemp.
		Working on a hack to allow VGA charcells to be shown even when
		not running with X11.
		Adding more x86 instructions.
20050508	x86 32-bit SIB addressing fix, and more instructions.
20050509	Adding more x86 instructions.
20050510	Minor documentation updates, and other updates (x86 stuff etc.)
20050511	More x86-related updates.
20050513	Various updates, mostly x86-related. (Trying to fix flag 
		calculation, factoring out the ugly shift/rotate code, and
		some other things.)
20050514	Adding support for loading some old i386 a.out executables.
		Finally beginning the cleanup of machine/PROM/bios dependant
		info.
		Some minor documentation updates.
		Trying to clean up ARCBIOS stuff a little.
20050515	Trying to make it possible to actually use more than one disk
		type per machine (floppy, ide, scsi).
		Trying to clean up the kbd vs PROM console stuff. (For PC and
		ARC emulation modes, mostly.)
		Beginning to add an 8259 interrupt controller, and connecting
		it to the x86 emulation.
20050516	The first x86 interrupts seem to work (keyboard stuff).
		Adding a 8253/8254 programmable interval timer skeleton.
		FreeDOS now reaches a command prompt and can be interacted
		with.
20050517	After some bugfixes, MS-DOS also (sometimes) reaches a
		command prompt now.
		Trying to fix the pckbc to work with MS-DOS' keyb.com, but no
		success yet.
20050518	Adding a simple 32-bit x86 MMU skeleton.
20050519	Some more work on the x86 stuff. (Beginning the work on paging,
		and various other fixes).
20050520	More updates. Working on dev_vga (4-bit graphics modes), adding
		40 columns support to the PC bios emulation.
		Trying to add support for resizing windows when switching
		between graphics modes.
20050521	Many more x86-related updates.
20050522	Correcting the initial stack pointer's sign-extension for
		ARCBIOS emulation (thanks to Alec Voropay for noticing the
		error).
		Continuing on the cleanup (ARCBIOS etc).
		dev_vga updates.
20050523	More x86 updates: trying to add some support for protected mode
		interrupts (via gate descriptors) and many other fixes.
		More ARCBIOS cleanup.
		Adding a device flag which indicates that reads cause no
		side-effects. (Useful for the "dump" command in the debugger,
		and other things.)
		Adding support for directly starting up x86 ELFs, skipping the
		bootloader stage. (Most ELFs, however, are not suitable for
		this.)
20050524	Adding simple 32-bit x86 TSS task switching, but no privilege
		level support yet.
		More work on dev_vga. A small "Copper bars" demo works. :-)
		Adding support for Trap Flag (single-step exceptions), at least
		in real mode, and various other x86-related fixes.
20050525	Adding a new disk image prefix (gH;S;) which can be used to
		override the default nr of heads and sectors per track.
20050527	Various bug fixes, more work on the x86 mode (stack change on
		interrupts between different priv.levels), and some minor
		documentation updates.
20050528	Various fixes (x86 stuff).
20050529	More x86 fixes. An OpenBSD/i386 bootfloppy reaches userland
		and can be interacted with (although there are problems with
		key repetition). NetBSD/i386 triggers a serious CISC-related
		problem: instruction fetches across page boundaries, where
		the later part isn't actually part of the instruction.
20050530	Various minor updates. (Documentation updates, etc.)
20050531	Adding some experimental code (experiments/new_test_*) which
		could be useful for dynamic (but not binary) translation in
		the future.
20050602	Adding a dummy ARM skeleton.
		Fixing the pckbc key repetition problem (by adding release
		scancodes for all keypresses).
20050603	Minor updates for the next release.
20050604	Release testing. Minor updates.

==============  RELEASE 0.3.3  ==============

20050604	There'll probably be a 0.3.3.1 release soon, with some very
		very tiny updates.


1 dpavlin 2 /*
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 dpavlin 6 * $Id: cpu.c,v 1.295 2005/06/02 00:08:41 debug Exp $
29 dpavlin 2 *
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 dpavlin 6 fatal("\ncpu_new(): unknown cpu type '%s'\n", cpu_type_name);
94 dpavlin 2 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 dpavlin 6 add_cpu_family(arm_cpu_family_init, ARCH_ARM);
555 dpavlin 2 add_cpu_family(hppa_cpu_family_init, ARCH_HPPA);
556     add_cpu_family(mips_cpu_family_init, ARCH_MIPS);
557     add_cpu_family(ppc_cpu_family_init, ARCH_PPC);
558     add_cpu_family(sparc_cpu_family_init, ARCH_SPARC);
559     add_cpu_family(urisc_cpu_family_init, ARCH_URISC);
560 dpavlin 4 add_cpu_family(x86_cpu_family_init, ARCH_X86);
561 dpavlin 2 }
562    

  ViewVC Help
Powered by ViewVC 1.1.26