--- trunk/src/cpus/cpu_sh.c 2007/10/08 16:21:06 33 +++ trunk/src/cpus/cpu_sh.c 2007/10/08 16:21:17 34 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2006 Anders Gavare. All rights reserved. + * Copyright (C) 2005-2007 Anders Gavare. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,7 +25,7 @@ * SUCH DAMAGE. * * - * $Id: cpu_sh.c,v 1.53 2006/10/31 11:07:05 debug Exp $ + * $Id: cpu_sh.c,v 1.60 2007/01/05 17:42:23 debug Exp $ * * Hitachi SuperH ("SH") CPU emulation. * @@ -42,6 +42,7 @@ #include "cpu.h" #include "device.h" #include "float_emul.h" +#include "interrupt.h" #include "machine.h" #include "memory.h" #include "misc.h" @@ -132,6 +133,9 @@ CPU_SETTINGS_ADD_REGISTER32("gbr", cpu->cd.sh.gbr); CPU_SETTINGS_ADD_REGISTER32("macl", cpu->cd.sh.macl); CPU_SETTINGS_ADD_REGISTER32("mach", cpu->cd.sh.mach); + CPU_SETTINGS_ADD_REGISTER32("expevt", cpu->cd.sh.expevt); + CPU_SETTINGS_ADD_REGISTER32("intevt", cpu->cd.sh.intevt); + CPU_SETTINGS_ADD_REGISTER32("tra", cpu->cd.sh.tra); CPU_SETTINGS_ADD_REGISTER32("fpscr", cpu->cd.sh.fpscr); CPU_SETTINGS_ADD_REGISTER32("fpul", cpu->cd.sh.fpul); for (i=0; icd.sh.utlb_lo[i]); } + /* Register the CPU's interrupts: */ + for (i=SH_INTEVT_NMI; i<0x1000; i+=0x20) { + struct interrupt template; + char name[100]; + snprintf(name, sizeof(name), "%s.irq[0x%x]", cpu->path, i); + memset(&template, 0, sizeof(template)); + template.line = i; + template.name = name; + template.extra = cpu; + template.interrupt_assert = sh_cpu_interrupt_assert; + template.interrupt_deassert = sh_cpu_interrupt_deassert; + interrupt_handler_register(&template); + } + /* SH4-specific memory mapped registers, TLBs, caches, etc: */ if (cpu->cd.sh.cpu_type.arch == 4) device_add(machine, "sh4"); @@ -175,6 +193,107 @@ /* + * sh_cpu_interrupt_assert(): + */ +void sh_cpu_interrupt_assert(struct interrupt *interrupt) +{ + struct cpu *cpu = interrupt->extra; + int irq_nr = interrupt->line; + int word_index, bit_index; + + /* + * Note: This gives higher interrupt priority to lower number + * interrupts. Hopefully this is correct. + */ + + if (cpu->cd.sh.int_to_assert == 0 || irq_nr < cpu->cd.sh.int_to_assert) + cpu->cd.sh.int_to_assert = irq_nr; + + /* + * TODO: Keep track of all pending interrupts at multiple levels... + * + * This is just a quick hack: + */ + cpu->cd.sh.int_level = 1; + if (irq_nr == SH_INTEVT_TMU0_TUNI0) + cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 12) & 0xf; + if (irq_nr == SH_INTEVT_TMU1_TUNI1) + cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 8) & 0xf; + if (irq_nr == SH_INTEVT_TMU2_TUNI2) + cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 4) & 0xf; + if (irq_nr >= SH4_INTEVT_SCIF_ERI && + irq_nr <= SH4_INTEVT_SCIF_TXI) + cpu->cd.sh.int_level = (cpu->cd.sh.intc_iprc >> 4) & 0xf; + + irq_nr /= 0x20; + word_index = irq_nr / (sizeof(uint32_t)*8); + bit_index = irq_nr & ((sizeof(uint32_t)*8) - 1); + + cpu->cd.sh.int_pending[word_index] |= (1 << bit_index); +} + + +/* + * sh_cpu_interrupt_deassert(): + */ +void sh_cpu_interrupt_deassert(struct interrupt *interrupt) +{ + struct cpu *cpu = interrupt->extra; + int irq_nr = interrupt->line; + int word_index, bit_index; + + if (cpu->cd.sh.int_to_assert == irq_nr) { + /* + * Rescan all interrupts to see if any are still asserted. + * + * Note: The scan only has to go from irq_nr + 0x20 to the max + * index, since any lower interrupt cannot be asserted + * at this time. + */ + int i, max = 0x1000; + cpu->cd.sh.int_to_assert = 0; + + for (i=irq_nr+0x20; icd.sh.int_pending[word_index] == 0) + i += (sizeof(uint32_t)*8 - 1) * 0x20; + else if (cpu->cd.sh.int_pending[word_index] + & (1 << bit_index)) { + cpu->cd.sh.int_to_assert = i; + + +/* Hack. TODO: Fix. */ + cpu->cd.sh.int_level = 1; + if (i == SH_INTEVT_TMU0_TUNI0) + cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 12) & 0xf; + if (i == SH_INTEVT_TMU1_TUNI1) + cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 8) & 0xf; + if (i == SH_INTEVT_TMU2_TUNI2) + cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 4) & 0xf; + if (i >= SH4_INTEVT_SCIF_ERI && + i <= SH4_INTEVT_SCIF_TXI) + cpu->cd.sh.int_level = (cpu->cd.sh.intc_iprc >> 4) & 0xf; + + + break; + } + } + } + + irq_nr /= 0x20; + word_index = irq_nr / (sizeof(uint32_t)*8); + bit_index = irq_nr & ((sizeof(uint32_t)*8) - 1); + + cpu->cd.sh.int_pending[word_index] &= ~(1 << bit_index); +} + + +/* * sh_cpu_list_available_types(): * * Print a list of available SH CPU types. @@ -402,90 +521,6 @@ /* - * sh_cpu_interrupt(): - * - * Note: This gives higher interrupt priority to lower number interrupts. - * Hopefully this is correct. - */ -int sh_cpu_interrupt(struct cpu *cpu, uint64_t irq_nr) -{ - int word_index, bit_index; - - if (cpu->cd.sh.int_to_assert == 0 || irq_nr < cpu->cd.sh.int_to_assert) - cpu->cd.sh.int_to_assert = irq_nr; - - /* - * TODO: Keep track of all pending interrupts at multiple levels... - * - * This is just a quick hack: - */ - cpu->cd.sh.int_level = 1; - if (irq_nr == SH_INTEVT_TMU0_TUNI0) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 12) & 0xf; - if (irq_nr == SH_INTEVT_TMU1_TUNI1) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 8) & 0xf; - if (irq_nr == SH_INTEVT_TMU2_TUNI2) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_ipra >> 4) & 0xf; - if (irq_nr >= SH4_INTEVT_SCIF_ERI && - irq_nr <= SH4_INTEVT_SCIF_TXI) - cpu->cd.sh.int_level = (cpu->cd.sh.intc_iprc >> 4) & 0xf; - - irq_nr /= 0x20; - word_index = irq_nr / (sizeof(uint32_t)*8); - bit_index = irq_nr & ((sizeof(uint32_t)*8) - 1); - - cpu->cd.sh.int_pending[word_index] |= (1 << bit_index); - - return 0; -} - - -/* - * sh_cpu_interrupt_ack(): - */ -int sh_cpu_interrupt_ack(struct cpu *cpu, uint64_t irq_nr) -{ - int word_index, bit_index; - - if (cpu->cd.sh.int_to_assert == irq_nr) { - /* - * Rescan all interrupts to see if any are still asserted. - * - * Note: The scan only has to go from irq_nr + 0x20 to the max - * index, since any lower interrupt cannot be asserted - * at this time. - */ - int i, max = 0x1000; - cpu->cd.sh.int_to_assert = 0; - - for (i=irq_nr+0x20; icd.sh.int_pending[word_index] == 0) - i += (sizeof(uint32_t)*8 - 1) * 0x20; - else if (cpu->cd.sh.int_pending[word_index] - & (1 << bit_index)) { - cpu->cd.sh.int_to_assert = i; - break; - } - } - } - - irq_nr /= 0x20; - word_index = irq_nr / (sizeof(uint32_t)*8); - bit_index = irq_nr & ((sizeof(uint32_t)*8) - 1); - - cpu->cd.sh.int_pending[word_index] &= ~(1 << bit_index); - - return 0; -} - - -/* * sh_update_sr(): * * Writes a new value to the status register. @@ -624,6 +659,17 @@ cpu->cd.sh.spc += sizeof(uint16_t); break; +#if 0 + /* Not yet. It's probably better to abort the emulator for now, + to detect this condition. It is unlikely to happen in normal + software. */ + case EXPEVT_RES_INST: + break; +#endif + + case EXPEVT_FPU_DISABLE: + break; + default:fatal("sh_exception(): exception 0x%x is not yet " "implemented.\n", expevt); exit(1); @@ -666,13 +712,19 @@ debug("stc\tsr,r%i\n", r8); else if (lo8 == 0x03) debug("bsrf\tr%i\n", r8); - else if (lo4 == 0x4) - debug("mov.b\tr%i,@(r0,r%i)\n", r4, r8); - else if (lo4 == 0x5) - debug("mov.w\tr%i,@(r0,r%i)\n", r4, r8); - else if (lo4 == 0x6) - debug("mov.l\tr%i,@(r0,r%i)\n", r4, r8); - else if (lo4 == 0x7) + else if (lo4 >= 4 && lo4 <= 6) { + if (lo4 == 0x4) + debug("mov.b\tr%i,@(r0,r%i)", r4, r8); + else if (lo4 == 0x5) + debug("mov.w\tr%i,@(r0,r%i)", r4, r8); + else if (lo4 == 0x6) + debug("mov.l\tr%i,@(r0,r%i)", r4, r8); + if (running) { + debug("\t; r0+r%i = 0x%08"PRIx32, r8, + cpu->cd.sh.r[0] + cpu->cd.sh.r[r8]); + } + debug("\n"); + } else if (lo4 == 0x7) debug("mul.l\tr%i,r%i\n", r4, r8); else if (iword == 0x0008) debug("clrt\n"); @@ -682,13 +734,19 @@ debug("sts\tmach,r%i\n", r8); else if (iword == 0x000b) debug("rts\n"); - else if (lo4 == 0xc) - debug("mov.b\t@(r0,r%i),r%i\n", r4, r8); - else if (lo4 == 0xd) - debug("mov.w\t@(r0,r%i),r%i\n", r4, r8); - else if (lo4 == 0xe) - debug("mov.l\t@(r0,r%i),r%i\n", r4, r8); - else if (lo8 == 0x12) + else if (lo4 >= 0xc && lo4 <= 0xe) { + if (lo4 == 0xc) + debug("mov.b\t@(r0,r%i),r%i", r4, r8); + else if (lo4 == 0xd) + debug("mov.w\t@(r0,r%i),r%i", r4, r8); + else if (lo4 == 0xe) + debug("mov.l\t@(r0,r%i),r%i", r4, r8); + if (running) { + debug("\t; r0+r%i = 0x%08"PRIx32, r4, + cpu->cd.sh.r[0] + cpu->cd.sh.r[r4]); + } + debug("\n"); + } else if (lo8 == 0x12) debug("stc\tgbr,r%i\n", r8); else if (iword == 0x0018) debug("sett\n"); @@ -740,13 +798,18 @@ debug("movca.l\tr0,@r%i\n", r8); else if (lo8 == 0xfa) debug("stc\tdbr,r%i\n", r8); - else if (iword == 0x00ff) + else if (iword == SH_INVALID_INSTR) debug("gxemul_dreamcast_prom_emul\n"); else debug("UNIMPLEMENTED hi4=0x%x, lo8=0x%02x\n", hi4, lo8); break; case 0x1: - debug("mov.l\tr%i,@(%i,r%i)\n", r4, lo4 * 4, r8); + debug("mov.l\tr%i,@(%i,r%i)", r4, lo4 * 4, r8); + if (running) { + debug("\t; r%i+%i = 0x%08"PRIx32, r8, lo4 * 4, + cpu->cd.sh.r[r8] + lo4 * 4); + } + debug("\n"); break; case 0x2: if (lo4 == 0x0) @@ -931,7 +994,12 @@ debug("UNIMPLEMENTED hi4=0x%x, lo8=0x%02x\n", hi4, lo8); break; case 0x5: - debug("mov.l\t@(%i,r%i),r%i\n", lo4 * 4, r4, r8); + debug("mov.l\t@(%i,r%i),r%i", lo4 * 4, r4, r8); + if (running) { + debug("\t; r%i+%i = 0x%08"PRIx32, r4, lo4 * 4, + cpu->cd.sh.r[r4] + lo4 * 4); + } + debug("\n"); break; case 0x6: if (lo4 == 0x0) @@ -973,14 +1041,26 @@ debug("add\t#%i,r%i\n", (int8_t)lo8, r8); break; case 0x8: - if (r8 == 0x0) { - debug("mov.b\tr0,@(%i,r%i)\n", lo4, r4); - } else if (r8 == 0x1) { - debug("mov.w\tr0,@(%i,r%i)\n", lo4 * 2, r4); - } else if (r8 == 0x4) { - debug("mov.b\t@(%i,r%i),r0\n", lo4, r4); - } else if (r8 == 0x5) { - debug("mov.w\t@(%i,r%i),r0\n", lo4 * 2, r4); + if (r8 == 0 || r8 == 4) { + if (r8 == 0x0) + debug("mov.b\tr0,@(%i,r%i)", lo4, r4); + else if (r8 == 0x4) + debug("mov.b\t@(%i,r%i),r0", lo4, r4); + if (running) { + debug("\t; r%i+%i = 0x%08"PRIx32, r4, lo4, + cpu->cd.sh.r[r4] + lo4); + } + debug("\n"); + } else if (r8 == 1 || r8 == 5) { + if (r8 == 0x1) + debug("mov.w\tr0,@(%i,r%i)", lo4 * 2, r4); + else if (r8 == 0x5) + debug("mov.w\t@(%i,r%i),r0", lo4 * 2, r4); + if (running) { + debug("\t; r%i+%i = 0x%08"PRIx32, r4, lo4 * 2, + cpu->cd.sh.r[r4] + lo4 * 2); + } + debug("\n"); } else if (r8 == 0x8) { debug("cmp/eq\t#%i,r0\n", (int8_t)lo8); } else if (r8 == 0x9 || r8 == 0xb || r8 == 0xd || r8 == 0xf) {