diff options
| author | fishsoupisgood <github@madingley.org> | 2019-04-29 01:17:54 +0100 | 
|---|---|---|
| committer | fishsoupisgood <github@madingley.org> | 2019-05-27 03:43:43 +0100 | 
| commit | 3f2546b2ef55b661fd8dd69682b38992225e86f6 (patch) | |
| tree | 65ca85f13617aee1dce474596800950f266a456c /target-sh4 | |
| download | qemu-master.tar.gz qemu-master.tar.bz2 qemu-master.zip  | |
Diffstat (limited to 'target-sh4')
| -rw-r--r-- | target-sh4/Makefile.objs | 2 | ||||
| -rw-r--r-- | target-sh4/README.sh4 | 150 | ||||
| -rw-r--r-- | target-sh4/cpu-qom.h | 94 | ||||
| -rw-r--r-- | target-sh4/cpu.c | 320 | ||||
| -rw-r--r-- | target-sh4/cpu.h | 367 | ||||
| -rw-r--r-- | target-sh4/gdbstub.c | 146 | ||||
| -rw-r--r-- | target-sh4/helper.c | 875 | ||||
| -rw-r--r-- | target-sh4/helper.h | 45 | ||||
| -rw-r--r-- | target-sh4/op_helper.c | 497 | ||||
| -rw-r--r-- | target-sh4/translate.c | 1984 | 
10 files changed, 4480 insertions, 0 deletions
diff --git a/target-sh4/Makefile.objs b/target-sh4/Makefile.objs new file mode 100644 index 00000000..a285358a --- /dev/null +++ b/target-sh4/Makefile.objs @@ -0,0 +1,2 @@ +obj-y += translate.o op_helper.o helper.o cpu.o +obj-y += gdbstub.o diff --git a/target-sh4/README.sh4 b/target-sh4/README.sh4 new file mode 100644 index 00000000..e578830f --- /dev/null +++ b/target-sh4/README.sh4 @@ -0,0 +1,150 @@ +qemu target:   sh4 +author:        Samuel Tardieu <sam@rfc1149.net> +last modified: Tue Dec  6 07:22:44 CET 2005 + +The sh4 target is not ready at all yet for integration in qemu. This +file describes the current state of implementation. + +Most places requiring attention and/or modification can be detected by +looking for "XXXXX" or "abort()". + +The sh4 core is located in target-sh4/*, while the 7750 peripheral +features (IO ports for example) are located in hw/sh7750.[ch]. The +main board description is in hw/shix.c, and the NAND flash in +hw/tc58128.[ch]. + +All the shortcomings indicated here will eventually be resolved. This +is a work in progress. Features are added in a semi-random order: if a +point is blocking to progress on booting the Linux kernel for the shix +board, it is addressed first; if feedback is necessary and no progress +can be made on blocking points until it is received, a random feature +is worked on. + +Goals +----- + +The primary model being worked on is the soft MMU target to be able to +emulate the Shix 2.0 board by Alexis Polti, described at +http://perso.enst.fr/~polti/realisations/shix20/ + +Ultimately, qemu will be coupled with a system C or a verilog +simulator to simulate the whole board functionalities. + +A sh4 user-mode has also somewhat started but will be worked on +afterwards. The goal is to automate tests for GNAT (GNU Ada) compiler +that I ported recently to the sh4-linux target. + +Registers +--------- + +16 general purpose registers are available at any time. The first 8 +registers are banked and the non-directly visible ones can be accessed +by privileged instructions. In qemu, we define 24 general purpose +registers and the code generation use either [0-7]+[8-15] or +[16-23]+[8-15] depending on the MD and RB flags in the sr +configuration register. + +Instructions +------------ + +Most sh4 instructions have been implemented. The missing ones at this +time are: +  - FPU related instructions +  - LDTLB to load a new MMU entry +  - SLEEP to put the processor in sleep mode + +Most instructions could be optimized a lot. This will be worked on +after the current model is fully functional unless debugging +convenience requires that it is done early. + +Many instructions did not have a chance to be tested yet. The plan is +to implement unit and regression testing of those in the future. + +MMU +--- + +The MMU is implemented in the sh4 core. MMU management has not been +tested at all yet. In the sh7750, it can be manipulated through memory +mapped registers and this part has not yet been implemented. + +Exceptions +---------- + +Exceptions are implemented as described in the sh4 reference manual +but have not been tested yet. They do not use qemu EXCP_ features +yet. + +IRQ +--- + +IRQ are not implemented yet. + +Peripheral features +------------------- + +  + Serial ports + +Configuration and use of the first serial port (SCI) without +interrupts is supported. Input has not yet been tested. + +Configuration of the second serial port (SCIF) is supported. FIFO +handling infrastructure has been started but is not completed yet. + +  + GPIO ports + +GPIO ports have been implemented. A registration function allows +external modules to register interest in some port changes (see +hw/tc58128.[ch] for an example) and will be called back. Interrupt +generation is not yet supported but some infrastructure is in place +for this purpose. Note that in the current model a peripheral module +cannot directly simulate a H->L->H input port transition and have an +interrupt generated on the low level. + +  + TC58128 NAND flash + +TC58128 NAND flash is partially implemented through GPIO ports. It +supports reading from flash. + +GDB +--- + +GDB remote target support has been implemented and lightly tested. + +Files +----- + +File names are hardcoded at this time. The bootloader must be stored in +shix_bios.bin in the current directory. The initial Linux image must +be stored in shix_linux_nand.bin in the current directory in NAND +format. Test files can be obtained from +http://perso.enst.fr/~polti/robot/ as well as the various datasheets I +use. + +qemu disk parameter on the command line is unused. You can supply any +existing image and it will be ignored. As the goal is to simulate an +embedded target, it is not clear how this parameter will be handled in +the future. + +To build an ELF kernel image from the NAND image, 16 bytes have to be +stripped off the end of every 528 bytes, keeping only 512 of them. The +following Python code snippet does it: + +#! /usr/bin/python + +def denand (infd, outfd): +    while True: +        d = infd.read (528) +        if not d: return +        outfd.write (d[:512]) + +if __name__ == '__main__': +    import sys +    denand (open (sys.argv[1], 'rb'), +            open (sys.argv[2], 'wb')) + +Style isssues +------------- + +There is currently a mix between my style (space before opening +parenthesis) and qemu style. This will be resolved before final +integration is proposed. diff --git a/target-sh4/cpu-qom.h b/target-sh4/cpu-qom.h new file mode 100644 index 00000000..6341238a --- /dev/null +++ b/target-sh4/cpu-qom.h @@ -0,0 +1,94 @@ +/* + * QEMU SuperH CPU + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ +#ifndef QEMU_SUPERH_CPU_QOM_H +#define QEMU_SUPERH_CPU_QOM_H + +#include "qom/cpu.h" + +#define TYPE_SUPERH_CPU "superh-cpu" + +#define TYPE_SH7750R_CPU "sh7750r-" TYPE_SUPERH_CPU +#define TYPE_SH7751R_CPU "sh7751r-" TYPE_SUPERH_CPU +#define TYPE_SH7785_CPU "sh7785-" TYPE_SUPERH_CPU + +#define SUPERH_CPU_CLASS(klass) \ +    OBJECT_CLASS_CHECK(SuperHCPUClass, (klass), TYPE_SUPERH_CPU) +#define SUPERH_CPU(obj) \ +    OBJECT_CHECK(SuperHCPU, (obj), TYPE_SUPERH_CPU) +#define SUPERH_CPU_GET_CLASS(obj) \ +    OBJECT_GET_CLASS(SuperHCPUClass, (obj), TYPE_SUPERH_CPU) + +/** + * SuperHCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * @name: The name. + * @pvr: Processor Version Register + * @prr: Processor Revision Register + * @cvr: Cache Version Register + * + * A SuperH CPU model. + */ +typedef struct SuperHCPUClass { +    /*< private >*/ +    CPUClass parent_class; +    /*< public >*/ + +    DeviceRealize parent_realize; +    void (*parent_reset)(CPUState *cpu); + +    const char *name; +    uint32_t pvr; +    uint32_t prr; +    uint32_t cvr; +} SuperHCPUClass; + +/** + * SuperHCPU: + * @env: #CPUSH4State + * + * A SuperH CPU. + */ +typedef struct SuperHCPU { +    /*< private >*/ +    CPUState parent_obj; +    /*< public >*/ + +    CPUSH4State env; +} SuperHCPU; + +static inline SuperHCPU *sh_env_get_cpu(CPUSH4State *env) +{ +    return container_of(env, SuperHCPU, env); +} + +#define ENV_GET_CPU(e) CPU(sh_env_get_cpu(e)) + +#define ENV_OFFSET offsetof(SuperHCPU, env) + +void superh_cpu_do_interrupt(CPUState *cpu); +bool superh_cpu_exec_interrupt(CPUState *cpu, int int_req); +void superh_cpu_dump_state(CPUState *cpu, FILE *f, +                           fprintf_function cpu_fprintf, int flags); +hwaddr superh_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int superh_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int superh_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + +#endif diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c new file mode 100644 index 00000000..64e4467c --- /dev/null +++ b/target-sh4/cpu.c @@ -0,0 +1,320 @@ +/* + * QEMU SuperH CPU + * + * Copyright (c) 2005 Samuel Tardieu + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "cpu.h" +#include "qemu-common.h" +#include "migration/vmstate.h" + + +static void superh_cpu_set_pc(CPUState *cs, vaddr value) +{ +    SuperHCPU *cpu = SUPERH_CPU(cs); + +    cpu->env.pc = value; +} + +static void superh_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ +    SuperHCPU *cpu = SUPERH_CPU(cs); + +    cpu->env.pc = tb->pc; +    cpu->env.flags = tb->flags; +} + +static bool superh_cpu_has_work(CPUState *cs) +{ +    return cs->interrupt_request & CPU_INTERRUPT_HARD; +} + +/* CPUClass::reset() */ +static void superh_cpu_reset(CPUState *s) +{ +    SuperHCPU *cpu = SUPERH_CPU(s); +    SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(cpu); +    CPUSH4State *env = &cpu->env; + +    scc->parent_reset(s); + +    memset(env, 0, offsetof(CPUSH4State, id)); +    tlb_flush(s, 1); + +    env->pc = 0xA0000000; +#if defined(CONFIG_USER_ONLY) +    env->fpscr = FPSCR_PR; /* value for userspace according to the kernel */ +    set_float_rounding_mode(float_round_nearest_even, &env->fp_status); /* ?! */ +#else +    env->sr = (1u << SR_MD) | (1u << SR_RB) | (1u << SR_BL) | +              (1u << SR_I3) | (1u << SR_I2) | (1u << SR_I1) | (1u << SR_I0); +    env->fpscr = FPSCR_DN | FPSCR_RM_ZERO; /* CPU reset value according to SH4 manual */ +    set_float_rounding_mode(float_round_to_zero, &env->fp_status); +    set_flush_to_zero(1, &env->fp_status); +#endif +    set_default_nan_mode(1, &env->fp_status); +} + +typedef struct SuperHCPUListState { +    fprintf_function cpu_fprintf; +    FILE *file; +} SuperHCPUListState; + +/* Sort alphabetically by type name. */ +static gint superh_cpu_list_compare(gconstpointer a, gconstpointer b) +{ +    ObjectClass *class_a = (ObjectClass *)a; +    ObjectClass *class_b = (ObjectClass *)b; +    const char *name_a, *name_b; + +    name_a = object_class_get_name(class_a); +    name_b = object_class_get_name(class_b); +    return strcmp(name_a, name_b); +} + +static void superh_cpu_list_entry(gpointer data, gpointer user_data) +{ +    ObjectClass *oc = data; +    SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); +    SuperHCPUListState *s = user_data; + +    (*s->cpu_fprintf)(s->file, "%s\n", +                      scc->name); +} + +void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ +    SuperHCPUListState s = { +        .cpu_fprintf = cpu_fprintf, +        .file = f, +    }; +    GSList *list; + +    list = object_class_get_list(TYPE_SUPERH_CPU, false); +    list = g_slist_sort(list, superh_cpu_list_compare); +    g_slist_foreach(list, superh_cpu_list_entry, &s); +    g_slist_free(list); +} + +static gint superh_cpu_name_compare(gconstpointer a, gconstpointer b) +{ +    const SuperHCPUClass *scc = SUPERH_CPU_CLASS(a); +    const char *name = b; + +    return strcasecmp(scc->name, name); +} + +static ObjectClass *superh_cpu_class_by_name(const char *cpu_model) +{ +    ObjectClass *oc; +    GSList *list, *item; + +    if (cpu_model == NULL) { +        return NULL; +    } +    if (strcasecmp(cpu_model, "any") == 0) { +        return object_class_by_name(TYPE_SH7750R_CPU); +    } + +    oc = object_class_by_name(cpu_model); +    if (oc != NULL && object_class_dynamic_cast(oc, TYPE_SUPERH_CPU) != NULL +        && !object_class_is_abstract(oc)) { +        return oc; +    } + +    oc = NULL; +    list = object_class_get_list(TYPE_SUPERH_CPU, false); +    item = g_slist_find_custom(list, cpu_model, superh_cpu_name_compare); +    if (item != NULL) { +        oc = item->data; +    } +    g_slist_free(list); +    return oc; +} + +SuperHCPU *cpu_sh4_init(const char *cpu_model) +{ +    return SUPERH_CPU(cpu_generic_init(TYPE_SUPERH_CPU, cpu_model)); +} + +static void sh7750r_cpu_initfn(Object *obj) +{ +    SuperHCPU *cpu = SUPERH_CPU(obj); +    CPUSH4State *env = &cpu->env; + +    env->id = SH_CPU_SH7750R; +    env->features = SH_FEATURE_BCR3_AND_BCR4; +} + +static void sh7750r_class_init(ObjectClass *oc, void *data) +{ +    SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); + +    scc->name = "SH7750R"; +    scc->pvr = 0x00050000; +    scc->prr = 0x00000100; +    scc->cvr = 0x00110000; +} + +static const TypeInfo sh7750r_type_info = { +    .name = TYPE_SH7750R_CPU, +    .parent = TYPE_SUPERH_CPU, +    .class_init = sh7750r_class_init, +    .instance_init = sh7750r_cpu_initfn, +}; + +static void sh7751r_cpu_initfn(Object *obj) +{ +    SuperHCPU *cpu = SUPERH_CPU(obj); +    CPUSH4State *env = &cpu->env; + +    env->id = SH_CPU_SH7751R; +    env->features = SH_FEATURE_BCR3_AND_BCR4; +} + +static void sh7751r_class_init(ObjectClass *oc, void *data) +{ +    SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); + +    scc->name = "SH7751R"; +    scc->pvr = 0x04050005; +    scc->prr = 0x00000113; +    scc->cvr = 0x00110000; /* Neutered caches, should be 0x20480000 */ +} + +static const TypeInfo sh7751r_type_info = { +    .name = TYPE_SH7751R_CPU, +    .parent = TYPE_SUPERH_CPU, +    .class_init = sh7751r_class_init, +    .instance_init = sh7751r_cpu_initfn, +}; + +static void sh7785_cpu_initfn(Object *obj) +{ +    SuperHCPU *cpu = SUPERH_CPU(obj); +    CPUSH4State *env = &cpu->env; + +    env->id = SH_CPU_SH7785; +    env->features = SH_FEATURE_SH4A; +} + +static void sh7785_class_init(ObjectClass *oc, void *data) +{ +    SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); + +    scc->name = "SH7785"; +    scc->pvr = 0x10300700; +    scc->prr = 0x00000200; +    scc->cvr = 0x71440211; +} + +static const TypeInfo sh7785_type_info = { +    .name = TYPE_SH7785_CPU, +    .parent = TYPE_SUPERH_CPU, +    .class_init = sh7785_class_init, +    .instance_init = sh7785_cpu_initfn, +}; + +static void superh_cpu_realizefn(DeviceState *dev, Error **errp) +{ +    CPUState *cs = CPU(dev); +    SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(dev); + +    cpu_reset(cs); +    qemu_init_vcpu(cs); + +    scc->parent_realize(dev, errp); +} + +static void superh_cpu_initfn(Object *obj) +{ +    CPUState *cs = CPU(obj); +    SuperHCPU *cpu = SUPERH_CPU(obj); +    CPUSH4State *env = &cpu->env; + +    cs->env_ptr = env; +    cpu_exec_init(cs, &error_abort); + +    env->movcal_backup_tail = &(env->movcal_backup); + +    if (tcg_enabled()) { +        sh4_translate_init(); +    } +} + +static const VMStateDescription vmstate_sh_cpu = { +    .name = "cpu", +    .unmigratable = 1, +}; + +static void superh_cpu_class_init(ObjectClass *oc, void *data) +{ +    DeviceClass *dc = DEVICE_CLASS(oc); +    CPUClass *cc = CPU_CLASS(oc); +    SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); + +    scc->parent_realize = dc->realize; +    dc->realize = superh_cpu_realizefn; + +    scc->parent_reset = cc->reset; +    cc->reset = superh_cpu_reset; + +    cc->class_by_name = superh_cpu_class_by_name; +    cc->has_work = superh_cpu_has_work; +    cc->do_interrupt = superh_cpu_do_interrupt; +    cc->cpu_exec_interrupt = superh_cpu_exec_interrupt; +    cc->dump_state = superh_cpu_dump_state; +    cc->set_pc = superh_cpu_set_pc; +    cc->synchronize_from_tb = superh_cpu_synchronize_from_tb; +    cc->gdb_read_register = superh_cpu_gdb_read_register; +    cc->gdb_write_register = superh_cpu_gdb_write_register; +#ifdef CONFIG_USER_ONLY +    cc->handle_mmu_fault = superh_cpu_handle_mmu_fault; +#else +    cc->get_phys_page_debug = superh_cpu_get_phys_page_debug; +#endif +    dc->vmsd = &vmstate_sh_cpu; +    cc->gdb_num_core_regs = 59; + +    /* +     * Reason: superh_cpu_initfn() calls cpu_exec_init(), which saves +     * the object in cpus -> dangling pointer after final +     * object_unref(). +     */ +    dc->cannot_destroy_with_object_finalize_yet = true; +} + +static const TypeInfo superh_cpu_type_info = { +    .name = TYPE_SUPERH_CPU, +    .parent = TYPE_CPU, +    .instance_size = sizeof(SuperHCPU), +    .instance_init = superh_cpu_initfn, +    .abstract = true, +    .class_size = sizeof(SuperHCPUClass), +    .class_init = superh_cpu_class_init, +}; + +static void superh_cpu_register_types(void) +{ +    type_register_static(&superh_cpu_type_info); +    type_register_static(&sh7750r_type_info); +    type_register_static(&sh7751r_type_info); +    type_register_static(&sh7785_type_info); +} + +type_init(superh_cpu_register_types) diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h new file mode 100644 index 00000000..34bb3d77 --- /dev/null +++ b/target-sh4/cpu.h @@ -0,0 +1,367 @@ +/* + *  SH4 emulation + * + *  Copyright (c) 2005 Samuel Tardieu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef _CPU_SH4_H +#define _CPU_SH4_H + +#include "config.h" +#include "qemu-common.h" + +#define TARGET_LONG_BITS 32 + +#define ELF_MACHINE	EM_SH + +/* CPU Subtypes */ +#define SH_CPU_SH7750  (1 << 0) +#define SH_CPU_SH7750S (1 << 1) +#define SH_CPU_SH7750R (1 << 2) +#define SH_CPU_SH7751  (1 << 3) +#define SH_CPU_SH7751R (1 << 4) +#define SH_CPU_SH7785  (1 << 5) +#define SH_CPU_SH7750_ALL (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7750R) +#define SH_CPU_SH7751_ALL (SH_CPU_SH7751 | SH_CPU_SH7751R) + +#define CPUArchState struct CPUSH4State + +#include "exec/cpu-defs.h" + +#include "fpu/softfloat.h" + +#define TARGET_PAGE_BITS 12	/* 4k XXXXX */ + +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +#define SR_MD 30 +#define SR_RB 29 +#define SR_BL 28 +#define SR_FD 15 +#define SR_M  9 +#define SR_Q  8 +#define SR_I3 7 +#define SR_I2 6 +#define SR_I1 5 +#define SR_I0 4 +#define SR_S  1 +#define SR_T  0 + +#define FPSCR_MASK             (0x003fffff) +#define FPSCR_FR               (1 << 21) +#define FPSCR_SZ               (1 << 20) +#define FPSCR_PR               (1 << 19) +#define FPSCR_DN               (1 << 18) +#define FPSCR_CAUSE_MASK       (0x3f << 12) +#define FPSCR_CAUSE_SHIFT      (12) +#define FPSCR_CAUSE_E          (1 << 17) +#define FPSCR_CAUSE_V          (1 << 16) +#define FPSCR_CAUSE_Z          (1 << 15) +#define FPSCR_CAUSE_O          (1 << 14) +#define FPSCR_CAUSE_U          (1 << 13) +#define FPSCR_CAUSE_I          (1 << 12) +#define FPSCR_ENABLE_MASK      (0x1f << 7) +#define FPSCR_ENABLE_SHIFT     (7) +#define FPSCR_ENABLE_V         (1 << 11) +#define FPSCR_ENABLE_Z         (1 << 10) +#define FPSCR_ENABLE_O         (1 << 9) +#define FPSCR_ENABLE_U         (1 << 8) +#define FPSCR_ENABLE_I         (1 << 7) +#define FPSCR_FLAG_MASK        (0x1f << 2) +#define FPSCR_FLAG_SHIFT       (2) +#define FPSCR_FLAG_V           (1 << 6) +#define FPSCR_FLAG_Z           (1 << 5) +#define FPSCR_FLAG_O           (1 << 4) +#define FPSCR_FLAG_U           (1 << 3) +#define FPSCR_FLAG_I           (1 << 2) +#define FPSCR_RM_MASK          (0x03 << 0) +#define FPSCR_RM_NEAREST       (0 << 0) +#define FPSCR_RM_ZERO          (1 << 0) + +#define DELAY_SLOT             (1 << 0) +#define DELAY_SLOT_CONDITIONAL (1 << 1) +#define DELAY_SLOT_TRUE        (1 << 2) +#define DELAY_SLOT_CLEARME     (1 << 3) +/* The dynamic value of the DELAY_SLOT_TRUE flag determines whether the jump + * after the delay slot should be taken or not. It is calculated from SR_T. + * + * It is unclear if it is permitted to modify the SR_T flag in a delay slot. + * The use of DELAY_SLOT_TRUE flag makes us accept such SR_T modification. + */ + +typedef struct tlb_t { +    uint32_t vpn;		/* virtual page number */ +    uint32_t ppn;		/* physical page number */ +    uint32_t size;		/* mapped page size in bytes */ +    uint8_t asid;		/* address space identifier */ +    uint8_t v:1;		/* validity */ +    uint8_t sz:2;		/* page size */ +    uint8_t sh:1;		/* share status */ +    uint8_t c:1;		/* cacheability */ +    uint8_t pr:2;		/* protection key */ +    uint8_t d:1;		/* dirty */ +    uint8_t wt:1;		/* write through */ +    uint8_t sa:3;		/* space attribute (PCMCIA) */ +    uint8_t tc:1;		/* timing control */ +} tlb_t; + +#define UTLB_SIZE 64 +#define ITLB_SIZE 4 + +#define NB_MMU_MODES 2 + +enum sh_features { +    SH_FEATURE_SH4A = 1, +    SH_FEATURE_BCR3_AND_BCR4 = 2, +}; + +typedef struct memory_content { +    uint32_t address; +    uint32_t value; +    struct memory_content *next; +} memory_content; + +typedef struct CPUSH4State { +    uint32_t flags;		/* general execution flags */ +    uint32_t gregs[24];		/* general registers */ +    float32 fregs[32];		/* floating point registers */ +    uint32_t sr;                /* status register (with T split out) */ +    uint32_t sr_m;              /* M bit of status register */ +    uint32_t sr_q;              /* Q bit of status register */ +    uint32_t sr_t;              /* T bit of status register */ +    uint32_t ssr;		/* saved status register */ +    uint32_t spc;		/* saved program counter */ +    uint32_t gbr;		/* global base register */ +    uint32_t vbr;		/* vector base register */ +    uint32_t sgr;		/* saved global register 15 */ +    uint32_t dbr;		/* debug base register */ +    uint32_t pc;		/* program counter */ +    uint32_t delayed_pc;	/* target of delayed jump */ +    uint32_t mach;		/* multiply and accumulate high */ +    uint32_t macl;		/* multiply and accumulate low */ +    uint32_t pr;		/* procedure register */ +    uint32_t fpscr;		/* floating point status/control register */ +    uint32_t fpul;		/* floating point communication register */ + +    /* float point status register */ +    float_status fp_status; + +    /* Those belong to the specific unit (SH7750) but are handled here */ +    uint32_t mmucr;		/* MMU control register */ +    uint32_t pteh;		/* page table entry high register */ +    uint32_t ptel;		/* page table entry low register */ +    uint32_t ptea;		/* page table entry assistance register */ +    uint32_t ttb;		/* tranlation table base register */ +    uint32_t tea;		/* TLB exception address register */ +    uint32_t tra;		/* TRAPA exception register */ +    uint32_t expevt;		/* exception event register */ +    uint32_t intevt;		/* interrupt event register */ + +    tlb_t itlb[ITLB_SIZE];	/* instruction translation table */ +    tlb_t utlb[UTLB_SIZE];	/* unified translation table */ + +    uint32_t ldst; + +    CPU_COMMON + +    /* Fields from here on are preserved over CPU reset. */ +    int id;			/* CPU model */ + +    /* The features that we should emulate. See sh_features above.  */ +    uint32_t features; + +    void *intc_handle; +    int in_sleep;		/* SR_BL ignored during sleep */ +    memory_content *movcal_backup; +    memory_content **movcal_backup_tail; +} CPUSH4State; + +#include "cpu-qom.h" + +void sh4_translate_init(void); +SuperHCPU *cpu_sh4_init(const char *cpu_model); +int cpu_sh4_exec(CPUState *s); +int cpu_sh4_signal_handler(int host_signum, void *pinfo, +                           void *puc); +int superh_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, +                                int mmu_idx); + +void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf); +#if !defined(CONFIG_USER_ONLY) +void cpu_sh4_invalidate_tlb(CPUSH4State *s); +uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s, +                                       hwaddr addr); +void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, hwaddr addr, +                                    uint32_t mem_value); +uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s, +                                       hwaddr addr); +void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, hwaddr addr, +                                    uint32_t mem_value); +uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s, +                                       hwaddr addr); +void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr, +                                    uint32_t mem_value); +uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s, +                                       hwaddr addr); +void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr, +                                    uint32_t mem_value); +#endif + +int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr); + +void cpu_load_tlb(CPUSH4State * env); + +#define cpu_init(cpu_model) CPU(cpu_sh4_init(cpu_model)) + +#define cpu_exec cpu_sh4_exec +#define cpu_gen_code cpu_sh4_gen_code +#define cpu_signal_handler cpu_sh4_signal_handler +#define cpu_list sh4_cpu_list + +/* MMU modes definitions */ +#define MMU_MODE0_SUFFIX _kernel +#define MMU_MODE1_SUFFIX _user +#define MMU_USER_IDX 1 +static inline int cpu_mmu_index (CPUSH4State *env) +{ +    return (env->sr & (1u << SR_MD)) == 0 ? 1 : 0; +} + +#include "exec/cpu-all.h" + +/* Memory access type */ +enum { +    /* Privilege */ +    ACCESS_PRIV = 0x01, +    /* Direction */ +    ACCESS_WRITE = 0x02, +    /* Type of instruction */ +    ACCESS_CODE = 0x10, +    ACCESS_INT = 0x20 +}; + +/* MMU control register */ +#define MMUCR    0x1F000010 +#define MMUCR_AT (1<<0) +#define MMUCR_TI (1<<2) +#define MMUCR_SV (1<<8) +#define MMUCR_URC_BITS (6) +#define MMUCR_URC_OFFSET (10) +#define MMUCR_URC_SIZE (1 << MMUCR_URC_BITS) +#define MMUCR_URC_MASK (((MMUCR_URC_SIZE) - 1) << MMUCR_URC_OFFSET) +static inline int cpu_mmucr_urc (uint32_t mmucr) +{ +    return ((mmucr & MMUCR_URC_MASK) >> MMUCR_URC_OFFSET); +} + +/* PTEH : Page Translation Entry High register */ +#define PTEH_ASID_BITS (8) +#define PTEH_ASID_SIZE (1 << PTEH_ASID_BITS) +#define PTEH_ASID_MASK (PTEH_ASID_SIZE - 1) +#define cpu_pteh_asid(pteh) ((pteh) & PTEH_ASID_MASK) +#define PTEH_VPN_BITS (22) +#define PTEH_VPN_OFFSET (10) +#define PTEH_VPN_SIZE (1 << PTEH_VPN_BITS) +#define PTEH_VPN_MASK (((PTEH_VPN_SIZE) - 1) << PTEH_VPN_OFFSET) +static inline int cpu_pteh_vpn (uint32_t pteh) +{ +    return ((pteh & PTEH_VPN_MASK) >> PTEH_VPN_OFFSET); +} + +/* PTEL : Page Translation Entry Low register */ +#define PTEL_V        (1 << 8) +#define cpu_ptel_v(ptel) (((ptel) & PTEL_V) >> 8) +#define PTEL_C        (1 << 3) +#define cpu_ptel_c(ptel) (((ptel) & PTEL_C) >> 3) +#define PTEL_D        (1 << 2) +#define cpu_ptel_d(ptel) (((ptel) & PTEL_D) >> 2) +#define PTEL_SH       (1 << 1) +#define cpu_ptel_sh(ptel)(((ptel) & PTEL_SH) >> 1) +#define PTEL_WT       (1 << 0) +#define cpu_ptel_wt(ptel) ((ptel) & PTEL_WT) + +#define PTEL_SZ_HIGH_OFFSET  (7) +#define PTEL_SZ_HIGH  (1 << PTEL_SZ_HIGH_OFFSET) +#define PTEL_SZ_LOW_OFFSET   (4) +#define PTEL_SZ_LOW   (1 << PTEL_SZ_LOW_OFFSET) +static inline int cpu_ptel_sz (uint32_t ptel) +{ +    int sz; +    sz = (ptel & PTEL_SZ_HIGH) >> PTEL_SZ_HIGH_OFFSET; +    sz <<= 1; +    sz |= (ptel & PTEL_SZ_LOW) >> PTEL_SZ_LOW_OFFSET; +    return sz; +} + +#define PTEL_PPN_BITS (19) +#define PTEL_PPN_OFFSET (10) +#define PTEL_PPN_SIZE (1 << PTEL_PPN_BITS) +#define PTEL_PPN_MASK (((PTEL_PPN_SIZE) - 1) << PTEL_PPN_OFFSET) +static inline int cpu_ptel_ppn (uint32_t ptel) +{ +    return ((ptel & PTEL_PPN_MASK) >> PTEL_PPN_OFFSET); +} + +#define PTEL_PR_BITS   (2) +#define PTEL_PR_OFFSET (5) +#define PTEL_PR_SIZE (1 << PTEL_PR_BITS) +#define PTEL_PR_MASK (((PTEL_PR_SIZE) - 1) << PTEL_PR_OFFSET) +static inline int cpu_ptel_pr (uint32_t ptel) +{ +    return ((ptel & PTEL_PR_MASK) >> PTEL_PR_OFFSET); +} + +/* PTEA : Page Translation Entry Assistance register */ +#define PTEA_SA_BITS (3) +#define PTEA_SA_SIZE (1 << PTEA_SA_BITS) +#define PTEA_SA_MASK (PTEA_SA_SIZE - 1) +#define cpu_ptea_sa(ptea) ((ptea) & PTEA_SA_MASK) +#define PTEA_TC        (1 << 3) +#define cpu_ptea_tc(ptea) (((ptea) & PTEA_TC) >> 3) + +#define TB_FLAG_PENDING_MOVCA  (1 << 4) + +static inline target_ulong cpu_read_sr(CPUSH4State *env) +{ +    return env->sr | (env->sr_m << SR_M) | +                     (env->sr_q << SR_Q) | +                     (env->sr_t << SR_T); +} + +static inline void cpu_write_sr(CPUSH4State *env, target_ulong sr) +{ +    env->sr_m = (sr >> SR_M) & 1; +    env->sr_q = (sr >> SR_Q) & 1; +    env->sr_t = (sr >> SR_T) & 1; +    env->sr = sr & ~((1u << SR_M) | (1u << SR_Q) | (1u << SR_T)); +} + +static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc, +                                        target_ulong *cs_base, int *flags) +{ +    *pc = env->pc; +    *cs_base = 0; +    *flags = (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL +                    | DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME))   /* Bits  0- 3 */ +            | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR))  /* Bits 19-21 */ +            | (env->sr & ((1u << SR_MD) | (1u << SR_RB)))      /* Bits 29-30 */ +            | (env->sr & (1u << SR_FD))                        /* Bit 15 */ +            | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 4 */ +} + +#include "exec/exec-all.h" + +#endif				/* _CPU_SH4_H */ diff --git a/target-sh4/gdbstub.c b/target-sh4/gdbstub.c new file mode 100644 index 00000000..a365a27a --- /dev/null +++ b/target-sh4/gdbstub.c @@ -0,0 +1,146 @@ +/* + * SuperH gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +/* Hint: Use "set architecture sh4" in GDB to see fpu registers */ +/* FIXME: We should use XML for this.  */ + +int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ +    SuperHCPU *cpu = SUPERH_CPU(cs); +    CPUSH4State *env = &cpu->env; + +    switch (n) { +    case 0 ... 7: +        if ((env->sr & (1u << SR_MD)) && (env->sr & (1u << SR_RB))) { +            return gdb_get_regl(mem_buf, env->gregs[n + 16]); +        } else { +            return gdb_get_regl(mem_buf, env->gregs[n]); +        } +    case 8 ... 15: +        return gdb_get_regl(mem_buf, env->gregs[n]); +    case 16: +        return gdb_get_regl(mem_buf, env->pc); +    case 17: +        return gdb_get_regl(mem_buf, env->pr); +    case 18: +        return gdb_get_regl(mem_buf, env->gbr); +    case 19: +        return gdb_get_regl(mem_buf, env->vbr); +    case 20: +        return gdb_get_regl(mem_buf, env->mach); +    case 21: +        return gdb_get_regl(mem_buf, env->macl); +    case 22: +        return gdb_get_regl(mem_buf, cpu_read_sr(env)); +    case 23: +        return gdb_get_regl(mem_buf, env->fpul); +    case 24: +        return gdb_get_regl(mem_buf, env->fpscr); +    case 25 ... 40: +        if (env->fpscr & FPSCR_FR) { +            stfl_p(mem_buf, env->fregs[n - 9]); +        } else { +            stfl_p(mem_buf, env->fregs[n - 25]); +        } +        return 4; +    case 41: +        return gdb_get_regl(mem_buf, env->ssr); +    case 42: +        return gdb_get_regl(mem_buf, env->spc); +    case 43 ... 50: +        return gdb_get_regl(mem_buf, env->gregs[n - 43]); +    case 51 ... 58: +        return gdb_get_regl(mem_buf, env->gregs[n - (51 - 16)]); +    } + +    return 0; +} + +int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ +    SuperHCPU *cpu = SUPERH_CPU(cs); +    CPUSH4State *env = &cpu->env; + +    switch (n) { +    case 0 ... 7: +        if ((env->sr & (1u << SR_MD)) && (env->sr & (1u << SR_RB))) { +            env->gregs[n + 16] = ldl_p(mem_buf); +        } else { +            env->gregs[n] = ldl_p(mem_buf); +        } +        break; +    case 8 ... 15: +        env->gregs[n] = ldl_p(mem_buf); +        break; +    case 16: +        env->pc = ldl_p(mem_buf); +        break; +    case 17: +        env->pr = ldl_p(mem_buf); +        break; +    case 18: +        env->gbr = ldl_p(mem_buf); +        break; +    case 19: +        env->vbr = ldl_p(mem_buf); +        break; +    case 20: +        env->mach = ldl_p(mem_buf); +        break; +    case 21: +        env->macl = ldl_p(mem_buf); +        break; +    case 22: +        cpu_write_sr(env, ldl_p(mem_buf)); +        break; +    case 23: +        env->fpul = ldl_p(mem_buf); +        break; +    case 24: +        env->fpscr = ldl_p(mem_buf); +        break; +    case 25 ... 40: +        if (env->fpscr & FPSCR_FR) { +            env->fregs[n - 9] = ldfl_p(mem_buf); +        } else { +            env->fregs[n - 25] = ldfl_p(mem_buf); +        } +        break; +    case 41: +        env->ssr = ldl_p(mem_buf); +        break; +    case 42: +        env->spc = ldl_p(mem_buf); +        break; +    case 43 ... 50: +        env->gregs[n - 43] = ldl_p(mem_buf); +        break; +    case 51 ... 58: +        env->gregs[n - (51 - 16)] = ldl_p(mem_buf); +        break; +    default: +        return 0; +    } + +    return 4; +} diff --git a/target-sh4/helper.c b/target-sh4/helper.c new file mode 100644 index 00000000..a533f08e --- /dev/null +++ b/target-sh4/helper.c @@ -0,0 +1,875 @@ +/* + *  SH4 emulation + * + *  Copyright (c) 2005 Samuel Tardieu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <signal.h> + +#include "cpu.h" + +#if !defined(CONFIG_USER_ONLY) +#include "hw/sh4/sh_intc.h" +#endif + +#if defined(CONFIG_USER_ONLY) + +void superh_cpu_do_interrupt(CPUState *cs) +{ +    cs->exception_index = -1; +} + +int superh_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, +                                int mmu_idx) +{ +    SuperHCPU *cpu = SUPERH_CPU(cs); +    CPUSH4State *env = &cpu->env; + +    env->tea = address; +    cs->exception_index = -1; +    switch (rw) { +    case 0: +        cs->exception_index = 0x0a0; +        break; +    case 1: +        cs->exception_index = 0x0c0; +        break; +    case 2: +        cs->exception_index = 0x0a0; +        break; +    } +    return 1; +} + +int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) +{ +    /* For user mode, only U0 area is cachable. */ +    return !(addr & 0x80000000); +} + +#else /* !CONFIG_USER_ONLY */ + +#define MMU_OK                   0 +#define MMU_ITLB_MISS            (-1) +#define MMU_ITLB_MULTIPLE        (-2) +#define MMU_ITLB_VIOLATION       (-3) +#define MMU_DTLB_MISS_READ       (-4) +#define MMU_DTLB_MISS_WRITE      (-5) +#define MMU_DTLB_INITIAL_WRITE   (-6) +#define MMU_DTLB_VIOLATION_READ  (-7) +#define MMU_DTLB_VIOLATION_WRITE (-8) +#define MMU_DTLB_MULTIPLE        (-9) +#define MMU_DTLB_MISS            (-10) +#define MMU_IADDR_ERROR          (-11) +#define MMU_DADDR_ERROR_READ     (-12) +#define MMU_DADDR_ERROR_WRITE    (-13) + +void superh_cpu_do_interrupt(CPUState *cs) +{ +    SuperHCPU *cpu = SUPERH_CPU(cs); +    CPUSH4State *env = &cpu->env; +    int do_irq = cs->interrupt_request & CPU_INTERRUPT_HARD; +    int do_exp, irq_vector = cs->exception_index; + +    /* prioritize exceptions over interrupts */ + +    do_exp = cs->exception_index != -1; +    do_irq = do_irq && (cs->exception_index == -1); + +    if (env->sr & (1u << SR_BL)) { +        if (do_exp && cs->exception_index != 0x1e0) { +            cs->exception_index = 0x000; /* masked exception -> reset */ +        } +        if (do_irq && !env->in_sleep) { +            return; /* masked */ +        } +    } +    env->in_sleep = 0; + +    if (do_irq) { +        irq_vector = sh_intc_get_pending_vector(env->intc_handle, +						(env->sr >> 4) & 0xf); +        if (irq_vector == -1) { +            return; /* masked */ +	} +    } + +    if (qemu_loglevel_mask(CPU_LOG_INT)) { +	const char *expname; +        switch (cs->exception_index) { +	case 0x0e0: +	    expname = "addr_error"; +	    break; +	case 0x040: +	    expname = "tlb_miss"; +	    break; +	case 0x0a0: +	    expname = "tlb_violation"; +	    break; +	case 0x180: +	    expname = "illegal_instruction"; +	    break; +	case 0x1a0: +	    expname = "slot_illegal_instruction"; +	    break; +	case 0x800: +	    expname = "fpu_disable"; +	    break; +	case 0x820: +	    expname = "slot_fpu"; +	    break; +	case 0x100: +	    expname = "data_write"; +	    break; +	case 0x060: +	    expname = "dtlb_miss_write"; +	    break; +	case 0x0c0: +	    expname = "dtlb_violation_write"; +	    break; +	case 0x120: +	    expname = "fpu_exception"; +	    break; +	case 0x080: +	    expname = "initial_page_write"; +	    break; +	case 0x160: +	    expname = "trapa"; +	    break; +	default: +            expname = do_irq ? "interrupt" : "???"; +            break; +	} +	qemu_log("exception 0x%03x [%s] raised\n", +		  irq_vector, expname); +        log_cpu_state(cs, 0); +    } + +    env->ssr = cpu_read_sr(env); +    env->spc = env->pc; +    env->sgr = env->gregs[15]; +    env->sr |= (1u << SR_BL) | (1u << SR_MD) | (1u << SR_RB); + +    if (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { +        /* Branch instruction should be executed again before delay slot. */ +	env->spc -= 2; +	/* Clear flags for exception/interrupt routine. */ +	env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL | DELAY_SLOT_TRUE); +    } +    if (env->flags & DELAY_SLOT_CLEARME) +        env->flags = 0; + +    if (do_exp) { +        env->expevt = cs->exception_index; +        switch (cs->exception_index) { +        case 0x000: +        case 0x020: +        case 0x140: +            env->sr &= ~(1u << SR_FD); +            env->sr |= 0xf << 4; /* IMASK */ +            env->pc = 0xa0000000; +            break; +        case 0x040: +        case 0x060: +            env->pc = env->vbr + 0x400; +            break; +        case 0x160: +            env->spc += 2; /* special case for TRAPA */ +            /* fall through */ +        default: +            env->pc = env->vbr + 0x100; +            break; +        } +        return; +    } + +    if (do_irq) { +        env->intevt = irq_vector; +        env->pc = env->vbr + 0x600; +        return; +    } +} + +static void update_itlb_use(CPUSH4State * env, int itlbnb) +{ +    uint8_t or_mask = 0, and_mask = (uint8_t) - 1; + +    switch (itlbnb) { +    case 0: +	and_mask = 0x1f; +	break; +    case 1: +	and_mask = 0xe7; +	or_mask = 0x80; +	break; +    case 2: +	and_mask = 0xfb; +	or_mask = 0x50; +	break; +    case 3: +	or_mask = 0x2c; +	break; +    } + +    env->mmucr &= (and_mask << 24) | 0x00ffffff; +    env->mmucr |= (or_mask << 24); +} + +static int itlb_replacement(CPUSH4State * env) +{ +    SuperHCPU *cpu = sh_env_get_cpu(env); + +    if ((env->mmucr & 0xe0000000) == 0xe0000000) { +	return 0; +    } +    if ((env->mmucr & 0x98000000) == 0x18000000) { +	return 1; +    } +    if ((env->mmucr & 0x54000000) == 0x04000000) { +	return 2; +    } +    if ((env->mmucr & 0x2c000000) == 0x00000000) { +	return 3; +    } +    cpu_abort(CPU(cpu), "Unhandled itlb_replacement"); +} + +/* Find the corresponding entry in the right TLB +   Return entry, MMU_DTLB_MISS or MMU_DTLB_MULTIPLE +*/ +static int find_tlb_entry(CPUSH4State * env, target_ulong address, +			  tlb_t * entries, uint8_t nbtlb, int use_asid) +{ +    int match = MMU_DTLB_MISS; +    uint32_t start, end; +    uint8_t asid; +    int i; + +    asid = env->pteh & 0xff; + +    for (i = 0; i < nbtlb; i++) { +	if (!entries[i].v) +	    continue;		/* Invalid entry */ +	if (!entries[i].sh && use_asid && entries[i].asid != asid) +	    continue;		/* Bad ASID */ +	start = (entries[i].vpn << 10) & ~(entries[i].size - 1); +	end = start + entries[i].size - 1; +	if (address >= start && address <= end) {	/* Match */ +	    if (match != MMU_DTLB_MISS) +		return MMU_DTLB_MULTIPLE;	/* Multiple match */ +	    match = i; +	} +    } +    return match; +} + +static void increment_urc(CPUSH4State * env) +{ +    uint8_t urb, urc; + +    /* Increment URC */ +    urb = ((env->mmucr) >> 18) & 0x3f; +    urc = ((env->mmucr) >> 10) & 0x3f; +    urc++; +    if ((urb > 0 && urc > urb) || urc > (UTLB_SIZE - 1)) +	urc = 0; +    env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10); +} + +/* Copy and utlb entry into itlb +   Return entry +*/ +static int copy_utlb_entry_itlb(CPUSH4State *env, int utlb) +{ +    int itlb; + +    tlb_t * ientry; +    itlb = itlb_replacement(env); +    ientry = &env->itlb[itlb]; +    if (ientry->v) { +        tlb_flush_page(CPU(sh_env_get_cpu(env)), ientry->vpn << 10); +    } +    *ientry = env->utlb[utlb]; +    update_itlb_use(env, itlb); +    return itlb; +} + +/* Find itlb entry +   Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE +*/ +static int find_itlb_entry(CPUSH4State * env, target_ulong address, +                           int use_asid) +{ +    int e; + +    e = find_tlb_entry(env, address, env->itlb, ITLB_SIZE, use_asid); +    if (e == MMU_DTLB_MULTIPLE) { +	e = MMU_ITLB_MULTIPLE; +    } else if (e == MMU_DTLB_MISS) { +	e = MMU_ITLB_MISS; +    } else if (e >= 0) { +	update_itlb_use(env, e); +    } +    return e; +} + +/* Find utlb entry +   Return entry, MMU_DTLB_MISS, MMU_DTLB_MULTIPLE */ +static int find_utlb_entry(CPUSH4State * env, target_ulong address, int use_asid) +{ +    /* per utlb access */ +    increment_urc(env); + +    /* Return entry */ +    return find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid); +} + +/* Match address against MMU +   Return MMU_OK, MMU_DTLB_MISS_READ, MMU_DTLB_MISS_WRITE, +   MMU_DTLB_INITIAL_WRITE, MMU_DTLB_VIOLATION_READ, +   MMU_DTLB_VIOLATION_WRITE, MMU_ITLB_MISS, +   MMU_ITLB_MULTIPLE, MMU_ITLB_VIOLATION, +   MMU_IADDR_ERROR, MMU_DADDR_ERROR_READ, MMU_DADDR_ERROR_WRITE. +*/ +static int get_mmu_address(CPUSH4State * env, target_ulong * physical, +			   int *prot, target_ulong address, +			   int rw, int access_type) +{ +    int use_asid, n; +    tlb_t *matching = NULL; + +    use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD)); + +    if (rw == 2) { +        n = find_itlb_entry(env, address, use_asid); +	if (n >= 0) { +	    matching = &env->itlb[n]; +            if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) { +		n = MMU_ITLB_VIOLATION; +            } else { +		*prot = PAGE_EXEC; +            } +        } else { +            n = find_utlb_entry(env, address, use_asid); +            if (n >= 0) { +                n = copy_utlb_entry_itlb(env, n); +                matching = &env->itlb[n]; +                if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) { +                    n = MMU_ITLB_VIOLATION; +                } else { +                    *prot = PAGE_READ | PAGE_EXEC; +                    if ((matching->pr & 1) && matching->d) { +                        *prot |= PAGE_WRITE; +                    } +                } +            } else if (n == MMU_DTLB_MULTIPLE) { +                n = MMU_ITLB_MULTIPLE; +            } else if (n == MMU_DTLB_MISS) { +                n = MMU_ITLB_MISS; +            } +	} +    } else { +	n = find_utlb_entry(env, address, use_asid); +	if (n >= 0) { +	    matching = &env->utlb[n]; +            if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) { +                n = (rw == 1) ? MMU_DTLB_VIOLATION_WRITE : +                    MMU_DTLB_VIOLATION_READ; +            } else if ((rw == 1) && !(matching->pr & 1)) { +                n = MMU_DTLB_VIOLATION_WRITE; +            } else if ((rw == 1) && !matching->d) { +                n = MMU_DTLB_INITIAL_WRITE; +            } else { +                *prot = PAGE_READ; +                if ((matching->pr & 1) && matching->d) { +                    *prot |= PAGE_WRITE; +                } +            } +	} else if (n == MMU_DTLB_MISS) { +	    n = (rw == 1) ? MMU_DTLB_MISS_WRITE : +		MMU_DTLB_MISS_READ; +	} +    } +    if (n >= 0) { +	n = MMU_OK; +	*physical = ((matching->ppn << 10) & ~(matching->size - 1)) | +	    (address & (matching->size - 1)); +    } +    return n; +} + +static int get_physical_address(CPUSH4State * env, target_ulong * physical, +                                int *prot, target_ulong address, +                                int rw, int access_type) +{ +    /* P1, P2 and P4 areas do not use translation */ +    if ((address >= 0x80000000 && address < 0xc0000000) || +	address >= 0xe0000000) { +        if (!(env->sr & (1u << SR_MD)) +	    && (address < 0xe0000000 || address >= 0xe4000000)) { +	    /* Unauthorized access in user mode (only store queues are available) */ +	    fprintf(stderr, "Unauthorized access\n"); +	    if (rw == 0) +		return MMU_DADDR_ERROR_READ; +	    else if (rw == 1) +		return MMU_DADDR_ERROR_WRITE; +	    else +		return MMU_IADDR_ERROR; +	} +	if (address >= 0x80000000 && address < 0xc0000000) { +	    /* Mask upper 3 bits for P1 and P2 areas */ +	    *physical = address & 0x1fffffff; +	} else { +	    *physical = address; +	} +	*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; +	return MMU_OK; +    } + +    /* If MMU is disabled, return the corresponding physical page */ +    if (!(env->mmucr & MMUCR_AT)) { +	*physical = address & 0x1FFFFFFF; +	*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; +	return MMU_OK; +    } + +    /* We need to resort to the MMU */ +    return get_mmu_address(env, physical, prot, address, rw, access_type); +} + +int superh_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, +                                int mmu_idx) +{ +    SuperHCPU *cpu = SUPERH_CPU(cs); +    CPUSH4State *env = &cpu->env; +    target_ulong physical; +    int prot, ret, access_type; + +    access_type = ACCESS_INT; +    ret = +	get_physical_address(env, &physical, &prot, address, rw, +			     access_type); + +    if (ret != MMU_OK) { +	env->tea = address; +	if (ret != MMU_DTLB_MULTIPLE && ret != MMU_ITLB_MULTIPLE) { +	    env->pteh = (env->pteh & PTEH_ASID_MASK) | +		    (address & PTEH_VPN_MASK); +	} +	switch (ret) { +	case MMU_ITLB_MISS: +	case MMU_DTLB_MISS_READ: +            cs->exception_index = 0x040; +	    break; +	case MMU_DTLB_MULTIPLE: +	case MMU_ITLB_MULTIPLE: +            cs->exception_index = 0x140; +	    break; +	case MMU_ITLB_VIOLATION: +            cs->exception_index = 0x0a0; +	    break; +	case MMU_DTLB_MISS_WRITE: +            cs->exception_index = 0x060; +	    break; +	case MMU_DTLB_INITIAL_WRITE: +            cs->exception_index = 0x080; +	    break; +	case MMU_DTLB_VIOLATION_READ: +            cs->exception_index = 0x0a0; +	    break; +	case MMU_DTLB_VIOLATION_WRITE: +            cs->exception_index = 0x0c0; +	    break; +	case MMU_IADDR_ERROR: +	case MMU_DADDR_ERROR_READ: +            cs->exception_index = 0x0e0; +	    break; +	case MMU_DADDR_ERROR_WRITE: +            cs->exception_index = 0x100; +	    break; +	default: +            cpu_abort(cs, "Unhandled MMU fault"); +	} +	return 1; +    } + +    address &= TARGET_PAGE_MASK; +    physical &= TARGET_PAGE_MASK; + +    tlb_set_page(cs, address, physical, prot, mmu_idx, TARGET_PAGE_SIZE); +    return 0; +} + +hwaddr superh_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ +    SuperHCPU *cpu = SUPERH_CPU(cs); +    target_ulong physical; +    int prot; + +    get_physical_address(&cpu->env, &physical, &prot, addr, 0, 0); +    return physical; +} + +void cpu_load_tlb(CPUSH4State * env) +{ +    SuperHCPU *cpu = sh_env_get_cpu(env); +    int n = cpu_mmucr_urc(env->mmucr); +    tlb_t * entry = &env->utlb[n]; + +    if (entry->v) { +        /* Overwriting valid entry in utlb. */ +        target_ulong address = entry->vpn << 10; +        tlb_flush_page(CPU(cpu), address); +    } + +    /* Take values into cpu status from registers. */ +    entry->asid = (uint8_t)cpu_pteh_asid(env->pteh); +    entry->vpn  = cpu_pteh_vpn(env->pteh); +    entry->v    = (uint8_t)cpu_ptel_v(env->ptel); +    entry->ppn  = cpu_ptel_ppn(env->ptel); +    entry->sz   = (uint8_t)cpu_ptel_sz(env->ptel); +    switch (entry->sz) { +    case 0: /* 00 */ +        entry->size = 1024; /* 1K */ +        break; +    case 1: /* 01 */ +        entry->size = 1024 * 4; /* 4K */ +        break; +    case 2: /* 10 */ +        entry->size = 1024 * 64; /* 64K */ +        break; +    case 3: /* 11 */ +        entry->size = 1024 * 1024; /* 1M */ +        break; +    default: +        cpu_abort(CPU(cpu), "Unhandled load_tlb"); +        break; +    } +    entry->sh   = (uint8_t)cpu_ptel_sh(env->ptel); +    entry->c    = (uint8_t)cpu_ptel_c(env->ptel); +    entry->pr   = (uint8_t)cpu_ptel_pr(env->ptel); +    entry->d    = (uint8_t)cpu_ptel_d(env->ptel); +    entry->wt   = (uint8_t)cpu_ptel_wt(env->ptel); +    entry->sa   = (uint8_t)cpu_ptea_sa(env->ptea); +    entry->tc   = (uint8_t)cpu_ptea_tc(env->ptea); +} + + void cpu_sh4_invalidate_tlb(CPUSH4State *s) +{ +    int i; + +    /* UTLB */ +    for (i = 0; i < UTLB_SIZE; i++) { +        tlb_t * entry = &s->utlb[i]; +        entry->v = 0; +    } +    /* ITLB */ +    for (i = 0; i < ITLB_SIZE; i++) { +        tlb_t * entry = &s->itlb[i]; +        entry->v = 0; +    } + +    tlb_flush(CPU(sh_env_get_cpu(s)), 1); +} + +uint32_t cpu_sh4_read_mmaped_itlb_addr(CPUSH4State *s, +                                       hwaddr addr) +{ +    int index = (addr & 0x00000300) >> 8; +    tlb_t * entry = &s->itlb[index]; + +    return (entry->vpn  << 10) | +           (entry->v    <<  8) | +           (entry->asid); +} + +void cpu_sh4_write_mmaped_itlb_addr(CPUSH4State *s, hwaddr addr, +				    uint32_t mem_value) +{ +    uint32_t vpn = (mem_value & 0xfffffc00) >> 10; +    uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8); +    uint8_t asid = (uint8_t)(mem_value & 0x000000ff); + +    int index = (addr & 0x00000300) >> 8; +    tlb_t * entry = &s->itlb[index]; +    if (entry->v) { +        /* Overwriting valid entry in itlb. */ +        target_ulong address = entry->vpn << 10; +        tlb_flush_page(CPU(sh_env_get_cpu(s)), address); +    } +    entry->asid = asid; +    entry->vpn = vpn; +    entry->v = v; +} + +uint32_t cpu_sh4_read_mmaped_itlb_data(CPUSH4State *s, +                                       hwaddr addr) +{ +    int array = (addr & 0x00800000) >> 23; +    int index = (addr & 0x00000300) >> 8; +    tlb_t * entry = &s->itlb[index]; + +    if (array == 0) { +        /* ITLB Data Array 1 */ +        return (entry->ppn << 10) | +               (entry->v   <<  8) | +               (entry->pr  <<  5) | +               ((entry->sz & 1) <<  6) | +               ((entry->sz & 2) <<  4) | +               (entry->c   <<  3) | +               (entry->sh  <<  1); +    } else { +        /* ITLB Data Array 2 */ +        return (entry->tc << 1) | +               (entry->sa); +    } +} + +void cpu_sh4_write_mmaped_itlb_data(CPUSH4State *s, hwaddr addr, +                                    uint32_t mem_value) +{ +    int array = (addr & 0x00800000) >> 23; +    int index = (addr & 0x00000300) >> 8; +    tlb_t * entry = &s->itlb[index]; + +    if (array == 0) { +        /* ITLB Data Array 1 */ +        if (entry->v) { +            /* Overwriting valid entry in utlb. */ +            target_ulong address = entry->vpn << 10; +            tlb_flush_page(CPU(sh_env_get_cpu(s)), address); +        } +        entry->ppn = (mem_value & 0x1ffffc00) >> 10; +        entry->v   = (mem_value & 0x00000100) >> 8; +        entry->sz  = (mem_value & 0x00000080) >> 6 | +                     (mem_value & 0x00000010) >> 4; +        entry->pr  = (mem_value & 0x00000040) >> 5; +        entry->c   = (mem_value & 0x00000008) >> 3; +        entry->sh  = (mem_value & 0x00000002) >> 1; +    } else { +        /* ITLB Data Array 2 */ +        entry->tc  = (mem_value & 0x00000008) >> 3; +        entry->sa  = (mem_value & 0x00000007); +    } +} + +uint32_t cpu_sh4_read_mmaped_utlb_addr(CPUSH4State *s, +                                       hwaddr addr) +{ +    int index = (addr & 0x00003f00) >> 8; +    tlb_t * entry = &s->utlb[index]; + +    increment_urc(s); /* per utlb access */ + +    return (entry->vpn  << 10) | +           (entry->v    <<  8) | +           (entry->asid); +} + +void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr, +				    uint32_t mem_value) +{ +    int associate = addr & 0x0000080; +    uint32_t vpn = (mem_value & 0xfffffc00) >> 10; +    uint8_t d = (uint8_t)((mem_value & 0x00000200) >> 9); +    uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8); +    uint8_t asid = (uint8_t)(mem_value & 0x000000ff); +    int use_asid = !(s->mmucr & MMUCR_SV) || !(s->sr & (1u << SR_MD)); + +    if (associate) { +        int i; +	tlb_t * utlb_match_entry = NULL; +	int needs_tlb_flush = 0; + +	/* search UTLB */ +	for (i = 0; i < UTLB_SIZE; i++) { +            tlb_t * entry = &s->utlb[i]; +            if (!entry->v) +	        continue; + +            if (entry->vpn == vpn +                && (!use_asid || entry->asid == asid || entry->sh)) { +	        if (utlb_match_entry) { +                    CPUState *cs = CPU(sh_env_get_cpu(s)); + +		    /* Multiple TLB Exception */ +                    cs->exception_index = 0x140; +		    s->tea = addr; +		    break; +	        } +		if (entry->v && !v) +		    needs_tlb_flush = 1; +		entry->v = v; +		entry->d = d; +	        utlb_match_entry = entry; +	    } +	    increment_urc(s); /* per utlb access */ +	} + +	/* search ITLB */ +	for (i = 0; i < ITLB_SIZE; i++) { +            tlb_t * entry = &s->itlb[i]; +            if (entry->vpn == vpn +                && (!use_asid || entry->asid == asid || entry->sh)) { +	        if (entry->v && !v) +		    needs_tlb_flush = 1; +	        if (utlb_match_entry) +		    *entry = *utlb_match_entry; +	        else +		    entry->v = v; +		break; +	    } +	} + +        if (needs_tlb_flush) { +            tlb_flush_page(CPU(sh_env_get_cpu(s)), vpn << 10); +        } +         +    } else { +        int index = (addr & 0x00003f00) >> 8; +        tlb_t * entry = &s->utlb[index]; +	if (entry->v) { +            CPUState *cs = CPU(sh_env_get_cpu(s)); + +	    /* Overwriting valid entry in utlb. */ +            target_ulong address = entry->vpn << 10; +            tlb_flush_page(cs, address); +	} +	entry->asid = asid; +	entry->vpn = vpn; +	entry->d = d; +	entry->v = v; +	increment_urc(s); +    } +} + +uint32_t cpu_sh4_read_mmaped_utlb_data(CPUSH4State *s, +                                       hwaddr addr) +{ +    int array = (addr & 0x00800000) >> 23; +    int index = (addr & 0x00003f00) >> 8; +    tlb_t * entry = &s->utlb[index]; + +    increment_urc(s); /* per utlb access */ + +    if (array == 0) { +        /* ITLB Data Array 1 */ +        return (entry->ppn << 10) | +               (entry->v   <<  8) | +               (entry->pr  <<  5) | +               ((entry->sz & 1) <<  6) | +               ((entry->sz & 2) <<  4) | +               (entry->c   <<  3) | +               (entry->d   <<  2) | +               (entry->sh  <<  1) | +               (entry->wt); +    } else { +        /* ITLB Data Array 2 */ +        return (entry->tc << 1) | +               (entry->sa); +    } +} + +void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr, +                                    uint32_t mem_value) +{ +    int array = (addr & 0x00800000) >> 23; +    int index = (addr & 0x00003f00) >> 8; +    tlb_t * entry = &s->utlb[index]; + +    increment_urc(s); /* per utlb access */ + +    if (array == 0) { +        /* UTLB Data Array 1 */ +        if (entry->v) { +            /* Overwriting valid entry in utlb. */ +            target_ulong address = entry->vpn << 10; +            tlb_flush_page(CPU(sh_env_get_cpu(s)), address); +        } +        entry->ppn = (mem_value & 0x1ffffc00) >> 10; +        entry->v   = (mem_value & 0x00000100) >> 8; +        entry->sz  = (mem_value & 0x00000080) >> 6 | +                     (mem_value & 0x00000010) >> 4; +        entry->pr  = (mem_value & 0x00000060) >> 5; +        entry->c   = (mem_value & 0x00000008) >> 3; +        entry->d   = (mem_value & 0x00000004) >> 2; +        entry->sh  = (mem_value & 0x00000002) >> 1; +        entry->wt  = (mem_value & 0x00000001); +    } else { +        /* UTLB Data Array 2 */ +        entry->tc = (mem_value & 0x00000008) >> 3; +        entry->sa = (mem_value & 0x00000007); +    } +} + +int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) +{ +    int n; +    int use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD)); + +    /* check area */ +    if (env->sr & (1u << SR_MD)) { +        /* For previledged mode, P2 and P4 area is not cachable. */ +        if ((0xA0000000 <= addr && addr < 0xC0000000) || 0xE0000000 <= addr) +            return 0; +    } else { +        /* For user mode, only U0 area is cachable. */ +        if (0x80000000 <= addr) +            return 0; +    } + +    /* +     * TODO : Evaluate CCR and check if the cache is on or off. +     *        Now CCR is not in CPUSH4State, but in SH7750State. +     *        When you move the ccr into CPUSH4State, the code will be +     *        as follows. +     */ +#if 0 +    /* check if operand cache is enabled or not. */ +    if (!(env->ccr & 1)) +        return 0; +#endif + +    /* if MMU is off, no check for TLB. */ +    if (env->mmucr & MMUCR_AT) +        return 1; + +    /* check TLB */ +    n = find_tlb_entry(env, addr, env->itlb, ITLB_SIZE, use_asid); +    if (n >= 0) +        return env->itlb[n].c; + +    n = find_tlb_entry(env, addr, env->utlb, UTLB_SIZE, use_asid); +    if (n >= 0) +        return env->utlb[n].c; + +    return 0; +} + +#endif + +bool superh_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ +    if (interrupt_request & CPU_INTERRUPT_HARD) { +        superh_cpu_do_interrupt(cs); +        return true; +    } +    return false; +} diff --git a/target-sh4/helper.h b/target-sh4/helper.h new file mode 100644 index 00000000..c9bc4070 --- /dev/null +++ b/target-sh4/helper.h @@ -0,0 +1,45 @@ +DEF_HELPER_1(ldtlb, void, env) +DEF_HELPER_1(raise_illegal_instruction, noreturn, env) +DEF_HELPER_1(raise_slot_illegal_instruction, noreturn, env) +DEF_HELPER_1(raise_fpu_disable, noreturn, env) +DEF_HELPER_1(raise_slot_fpu_disable, noreturn, env) +DEF_HELPER_1(debug, noreturn, env) +DEF_HELPER_1(sleep, noreturn, env) +DEF_HELPER_2(trapa, noreturn, env, i32) + +DEF_HELPER_3(movcal, void, env, i32, i32) +DEF_HELPER_1(discard_movcal_backup, void, env) +DEF_HELPER_2(ocbi, void, env, i32) + +DEF_HELPER_3(macl, void, env, i32, i32) +DEF_HELPER_3(macw, void, env, i32, i32) + +DEF_HELPER_2(ld_fpscr, void, env, i32) + +DEF_HELPER_FLAGS_1(fabs_FT, TCG_CALL_NO_RWG_SE, f32, f32) +DEF_HELPER_FLAGS_1(fabs_DT, TCG_CALL_NO_RWG_SE, f64, f64) +DEF_HELPER_3(fadd_FT, f32, env, f32, f32) +DEF_HELPER_3(fadd_DT, f64, env, f64, f64) +DEF_HELPER_2(fcnvsd_FT_DT, f64, env, f32) +DEF_HELPER_2(fcnvds_DT_FT, f32, env, f64) + +DEF_HELPER_3(fcmp_eq_FT, void, env, f32, f32) +DEF_HELPER_3(fcmp_eq_DT, void, env, f64, f64) +DEF_HELPER_3(fcmp_gt_FT, void, env, f32, f32) +DEF_HELPER_3(fcmp_gt_DT, void, env, f64, f64) +DEF_HELPER_3(fdiv_FT, f32, env, f32, f32) +DEF_HELPER_3(fdiv_DT, f64, env, f64, f64) +DEF_HELPER_2(float_FT, f32, env, i32) +DEF_HELPER_2(float_DT, f64, env, i32) +DEF_HELPER_4(fmac_FT, f32, env, f32, f32, f32) +DEF_HELPER_3(fmul_FT, f32, env, f32, f32) +DEF_HELPER_3(fmul_DT, f64, env, f64, f64) +DEF_HELPER_FLAGS_1(fneg_T, TCG_CALL_NO_RWG_SE, f32, f32) +DEF_HELPER_3(fsub_FT, f32, env, f32, f32) +DEF_HELPER_3(fsub_DT, f64, env, f64, f64) +DEF_HELPER_2(fsqrt_FT, f32, env, f32) +DEF_HELPER_2(fsqrt_DT, f64, env, f64) +DEF_HELPER_2(ftrc_FT, i32, env, f32) +DEF_HELPER_2(ftrc_DT, i32, env, f64) +DEF_HELPER_3(fipr, void, env, i32, i32) +DEF_HELPER_2(ftrv, void, env, i32) diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c new file mode 100644 index 00000000..cbc11aec --- /dev/null +++ b/target-sh4/op_helper.c @@ -0,0 +1,497 @@ +/* + *  SH4 emulation + * + *  Copyright (c) 2005 Samuel Tardieu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include <assert.h> +#include <stdlib.h> +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" + +#ifndef CONFIG_USER_ONLY + +void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, +              uintptr_t retaddr) +{ +    int ret; + +    ret = superh_cpu_handle_mmu_fault(cs, addr, is_write, mmu_idx); +    if (ret) { +        /* now we have a real cpu fault */ +        if (retaddr) { +            cpu_restore_state(cs, retaddr); +        } +        cpu_loop_exit(cs); +    } +} + +#endif + +void helper_ldtlb(CPUSH4State *env) +{ +#ifdef CONFIG_USER_ONLY +    SuperHCPU *cpu = sh_env_get_cpu(env); + +    /* XXXXX */ +    cpu_abort(CPU(cpu), "Unhandled ldtlb"); +#else +    cpu_load_tlb(env); +#endif +} + +static inline void QEMU_NORETURN raise_exception(CPUSH4State *env, int index, +                                                 uintptr_t retaddr) +{ +    CPUState *cs = CPU(sh_env_get_cpu(env)); + +    cs->exception_index = index; +    if (retaddr) { +        cpu_restore_state(cs, retaddr); +    } +    cpu_loop_exit(cs); +} + +void helper_raise_illegal_instruction(CPUSH4State *env) +{ +    raise_exception(env, 0x180, 0); +} + +void helper_raise_slot_illegal_instruction(CPUSH4State *env) +{ +    raise_exception(env, 0x1a0, 0); +} + +void helper_raise_fpu_disable(CPUSH4State *env) +{ +    raise_exception(env, 0x800, 0); +} + +void helper_raise_slot_fpu_disable(CPUSH4State *env) +{ +    raise_exception(env, 0x820, 0); +} + +void helper_debug(CPUSH4State *env) +{ +    raise_exception(env, EXCP_DEBUG, 0); +} + +void helper_sleep(CPUSH4State *env) +{ +    CPUState *cs = CPU(sh_env_get_cpu(env)); + +    cs->halted = 1; +    env->in_sleep = 1; +    raise_exception(env, EXCP_HLT, 0); +} + +void helper_trapa(CPUSH4State *env, uint32_t tra) +{ +    env->tra = tra << 2; +    raise_exception(env, 0x160, 0); +} + +void helper_movcal(CPUSH4State *env, uint32_t address, uint32_t value) +{ +    if (cpu_sh4_is_cached (env, address)) +    { +	memory_content *r = malloc (sizeof(memory_content)); +	r->address = address; +	r->value = value; +	r->next = NULL; + +	*(env->movcal_backup_tail) = r; +	env->movcal_backup_tail = &(r->next); +    } +} + +void helper_discard_movcal_backup(CPUSH4State *env) +{ +    memory_content *current = env->movcal_backup; + +    while(current) +    { +	memory_content *next = current->next; +	free (current); +	env->movcal_backup = current = next; +	if (current == NULL) +	    env->movcal_backup_tail = &(env->movcal_backup); +    }  +} + +void helper_ocbi(CPUSH4State *env, uint32_t address) +{ +    memory_content **current = &(env->movcal_backup); +    while (*current) +    { +	uint32_t a = (*current)->address; +	if ((a & ~0x1F) == (address & ~0x1F)) +	{ +	    memory_content *next = (*current)->next; +            cpu_stl_data(env, a, (*current)->value); +	     +	    if (next == NULL) +	    { +		env->movcal_backup_tail = current; +	    } + +	    free (*current); +	    *current = next; +	    break; +	} +    } +} + +void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1) +{ +    int64_t res; + +    res = ((uint64_t) env->mach << 32) | env->macl; +    res += (int64_t) (int32_t) arg0 *(int64_t) (int32_t) arg1; +    env->mach = (res >> 32) & 0xffffffff; +    env->macl = res & 0xffffffff; +    if (env->sr & (1u << SR_S)) { +	if (res < 0) +	    env->mach |= 0xffff0000; +	else +	    env->mach &= 0x00007fff; +    } +} + +void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1) +{ +    int64_t res; + +    res = ((uint64_t) env->mach << 32) | env->macl; +    res += (int64_t) (int16_t) arg0 *(int64_t) (int16_t) arg1; +    env->mach = (res >> 32) & 0xffffffff; +    env->macl = res & 0xffffffff; +    if (env->sr & (1u << SR_S)) { +	if (res < -0x80000000) { +	    env->mach = 1; +	    env->macl = 0x80000000; +	} else if (res > 0x000000007fffffff) { +	    env->mach = 1; +	    env->macl = 0x7fffffff; +	} +    } +} + +void helper_ld_fpscr(CPUSH4State *env, uint32_t val) +{ +    env->fpscr = val & FPSCR_MASK; +    if ((val & FPSCR_RM_MASK) == FPSCR_RM_ZERO) { +	set_float_rounding_mode(float_round_to_zero, &env->fp_status); +    } else { +	set_float_rounding_mode(float_round_nearest_even, &env->fp_status); +    } +    set_flush_to_zero((val & FPSCR_DN) != 0, &env->fp_status); +} + +static void update_fpscr(CPUSH4State *env, uintptr_t retaddr) +{ +    int xcpt, cause, enable; + +    xcpt = get_float_exception_flags(&env->fp_status); + +    /* Clear the flag entries */ +    env->fpscr &= ~FPSCR_FLAG_MASK; + +    if (unlikely(xcpt)) { +        if (xcpt & float_flag_invalid) { +            env->fpscr |= FPSCR_FLAG_V; +        } +        if (xcpt & float_flag_divbyzero) { +            env->fpscr |= FPSCR_FLAG_Z; +        } +        if (xcpt & float_flag_overflow) { +            env->fpscr |= FPSCR_FLAG_O; +        } +        if (xcpt & float_flag_underflow) { +            env->fpscr |= FPSCR_FLAG_U; +        } +        if (xcpt & float_flag_inexact) { +            env->fpscr |= FPSCR_FLAG_I; +        } + +        /* Accumulate in cause entries */ +        env->fpscr |= (env->fpscr & FPSCR_FLAG_MASK) +                      << (FPSCR_CAUSE_SHIFT - FPSCR_FLAG_SHIFT); + +        /* Generate an exception if enabled */ +        cause = (env->fpscr & FPSCR_CAUSE_MASK) >> FPSCR_CAUSE_SHIFT; +        enable = (env->fpscr & FPSCR_ENABLE_MASK) >> FPSCR_ENABLE_SHIFT; +        if (cause & enable) { +            raise_exception(env, 0x120, retaddr); +        } +    } +} + +float32 helper_fabs_FT(float32 t0) +{ +    return float32_abs(t0); +} + +float64 helper_fabs_DT(float64 t0) +{ +    return float64_abs(t0); +} + +float32 helper_fadd_FT(CPUSH4State *env, float32 t0, float32 t1) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float32_add(t0, t1, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +float64 helper_fadd_DT(CPUSH4State *env, float64 t0, float64 t1) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float64_add(t0, t1, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +void helper_fcmp_eq_FT(CPUSH4State *env, float32 t0, float32 t1) +{ +    int relation; + +    set_float_exception_flags(0, &env->fp_status); +    relation = float32_compare(t0, t1, &env->fp_status); +    if (unlikely(relation == float_relation_unordered)) { +        update_fpscr(env, GETPC()); +    } else { +        env->sr_t = (relation == float_relation_equal); +    } +} + +void helper_fcmp_eq_DT(CPUSH4State *env, float64 t0, float64 t1) +{ +    int relation; + +    set_float_exception_flags(0, &env->fp_status); +    relation = float64_compare(t0, t1, &env->fp_status); +    if (unlikely(relation == float_relation_unordered)) { +        update_fpscr(env, GETPC()); +    } else { +        env->sr_t = (relation == float_relation_equal); +    } +} + +void helper_fcmp_gt_FT(CPUSH4State *env, float32 t0, float32 t1) +{ +    int relation; + +    set_float_exception_flags(0, &env->fp_status); +    relation = float32_compare(t0, t1, &env->fp_status); +    if (unlikely(relation == float_relation_unordered)) { +        update_fpscr(env, GETPC()); +    } else { +        env->sr_t = (relation == float_relation_greater); +    } +} + +void helper_fcmp_gt_DT(CPUSH4State *env, float64 t0, float64 t1) +{ +    int relation; + +    set_float_exception_flags(0, &env->fp_status); +    relation = float64_compare(t0, t1, &env->fp_status); +    if (unlikely(relation == float_relation_unordered)) { +        update_fpscr(env, GETPC()); +    } else { +        env->sr_t = (relation == float_relation_greater); +    } +} + +float64 helper_fcnvsd_FT_DT(CPUSH4State *env, float32 t0) +{ +    float64 ret; +    set_float_exception_flags(0, &env->fp_status); +    ret = float32_to_float64(t0, &env->fp_status); +    update_fpscr(env, GETPC()); +    return ret; +} + +float32 helper_fcnvds_DT_FT(CPUSH4State *env, float64 t0) +{ +    float32 ret; +    set_float_exception_flags(0, &env->fp_status); +    ret = float64_to_float32(t0, &env->fp_status); +    update_fpscr(env, GETPC()); +    return ret; +} + +float32 helper_fdiv_FT(CPUSH4State *env, float32 t0, float32 t1) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float32_div(t0, t1, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +float64 helper_fdiv_DT(CPUSH4State *env, float64 t0, float64 t1) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float64_div(t0, t1, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +float32 helper_float_FT(CPUSH4State *env, uint32_t t0) +{ +    float32 ret; +    set_float_exception_flags(0, &env->fp_status); +    ret = int32_to_float32(t0, &env->fp_status); +    update_fpscr(env, GETPC()); +    return ret; +} + +float64 helper_float_DT(CPUSH4State *env, uint32_t t0) +{ +    float64 ret; +    set_float_exception_flags(0, &env->fp_status); +    ret = int32_to_float64(t0, &env->fp_status); +    update_fpscr(env, GETPC()); +    return ret; +} + +float32 helper_fmac_FT(CPUSH4State *env, float32 t0, float32 t1, float32 t2) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float32_muladd(t0, t1, t2, 0, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +float32 helper_fmul_FT(CPUSH4State *env, float32 t0, float32 t1) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float32_mul(t0, t1, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +float64 helper_fmul_DT(CPUSH4State *env, float64 t0, float64 t1) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float64_mul(t0, t1, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +float32 helper_fneg_T(float32 t0) +{ +    return float32_chs(t0); +} + +float32 helper_fsqrt_FT(CPUSH4State *env, float32 t0) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float32_sqrt(t0, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +float64 helper_fsqrt_DT(CPUSH4State *env, float64 t0) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float64_sqrt(t0, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +float32 helper_fsub_FT(CPUSH4State *env, float32 t0, float32 t1) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float32_sub(t0, t1, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +float64 helper_fsub_DT(CPUSH4State *env, float64 t0, float64 t1) +{ +    set_float_exception_flags(0, &env->fp_status); +    t0 = float64_sub(t0, t1, &env->fp_status); +    update_fpscr(env, GETPC()); +    return t0; +} + +uint32_t helper_ftrc_FT(CPUSH4State *env, float32 t0) +{ +    uint32_t ret; +    set_float_exception_flags(0, &env->fp_status); +    ret = float32_to_int32_round_to_zero(t0, &env->fp_status); +    update_fpscr(env, GETPC()); +    return ret; +} + +uint32_t helper_ftrc_DT(CPUSH4State *env, float64 t0) +{ +    uint32_t ret; +    set_float_exception_flags(0, &env->fp_status); +    ret = float64_to_int32_round_to_zero(t0, &env->fp_status); +    update_fpscr(env, GETPC()); +    return ret; +} + +void helper_fipr(CPUSH4State *env, uint32_t m, uint32_t n) +{ +    int bank, i; +    float32 r, p; + +    bank = (env->sr & FPSCR_FR) ? 16 : 0; +    r = float32_zero; +    set_float_exception_flags(0, &env->fp_status); + +    for (i = 0 ; i < 4 ; i++) { +        p = float32_mul(env->fregs[bank + m + i], +                        env->fregs[bank + n + i], +                        &env->fp_status); +        r = float32_add(r, p, &env->fp_status); +    } +    update_fpscr(env, GETPC()); + +    env->fregs[bank + n + 3] = r; +} + +void helper_ftrv(CPUSH4State *env, uint32_t n) +{ +    int bank_matrix, bank_vector; +    int i, j; +    float32 r[4]; +    float32 p; + +    bank_matrix = (env->sr & FPSCR_FR) ? 0 : 16; +    bank_vector = (env->sr & FPSCR_FR) ? 16 : 0; +    set_float_exception_flags(0, &env->fp_status); +    for (i = 0 ; i < 4 ; i++) { +        r[i] = float32_zero; +        for (j = 0 ; j < 4 ; j++) { +            p = float32_mul(env->fregs[bank_matrix + 4 * j + i], +                            env->fregs[bank_vector + j], +                            &env->fp_status); +            r[i] = float32_add(r[i], p, &env->fp_status); +        } +    } +    update_fpscr(env, GETPC()); + +    for (i = 0 ; i < 4 ; i++) { +        env->fregs[bank_vector + i] = r[i]; +    } +} diff --git a/target-sh4/translate.c b/target-sh4/translate.c new file mode 100644 index 00000000..3b4a1b5c --- /dev/null +++ b/target-sh4/translate.c @@ -0,0 +1,1984 @@ +/* + *  SH4 translation + * + *  Copyright (c) 2005 Samuel Tardieu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#define DEBUG_DISAS + +#include "cpu.h" +#include "disas/disas.h" +#include "tcg-op.h" +#include "exec/cpu_ldst.h" + +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" + +#include "trace-tcg.h" + + +typedef struct DisasContext { +    struct TranslationBlock *tb; +    target_ulong pc; +    uint16_t opcode; +    uint32_t flags; +    int bstate; +    int memidx; +    uint32_t delayed_pc; +    int singlestep_enabled; +    uint32_t features; +    int has_movcal; +} DisasContext; + +#if defined(CONFIG_USER_ONLY) +#define IS_USER(ctx) 1 +#else +#define IS_USER(ctx) (!(ctx->flags & (1u << SR_MD))) +#endif + +enum { +    BS_NONE     = 0, /* We go out of the TB without reaching a branch or an +                      * exception condition +                      */ +    BS_STOP     = 1, /* We want to stop translation for any reason */ +    BS_BRANCH   = 2, /* We reached a branch condition     */ +    BS_EXCP     = 3, /* We reached an exception condition */ +}; + +/* global register indexes */ +static TCGv_ptr cpu_env; +static TCGv cpu_gregs[24]; +static TCGv cpu_sr, cpu_sr_m, cpu_sr_q, cpu_sr_t; +static TCGv cpu_pc, cpu_ssr, cpu_spc, cpu_gbr; +static TCGv cpu_vbr, cpu_sgr, cpu_dbr, cpu_mach, cpu_macl; +static TCGv cpu_pr, cpu_fpscr, cpu_fpul, cpu_ldst; +static TCGv cpu_fregs[32]; + +/* internal register indexes */ +static TCGv cpu_flags, cpu_delayed_pc; + +static uint32_t gen_opc_hflags[OPC_BUF_SIZE]; + +#include "exec/gen-icount.h" + +void sh4_translate_init(void) +{ +    int i; +    static int done_init = 0; +    static const char * const gregnames[24] = { +        "R0_BANK0", "R1_BANK0", "R2_BANK0", "R3_BANK0", +        "R4_BANK0", "R5_BANK0", "R6_BANK0", "R7_BANK0", +        "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", +        "R0_BANK1", "R1_BANK1", "R2_BANK1", "R3_BANK1", +        "R4_BANK1", "R5_BANK1", "R6_BANK1", "R7_BANK1" +    }; +    static const char * const fregnames[32] = { +         "FPR0_BANK0",  "FPR1_BANK0",  "FPR2_BANK0",  "FPR3_BANK0", +         "FPR4_BANK0",  "FPR5_BANK0",  "FPR6_BANK0",  "FPR7_BANK0", +         "FPR8_BANK0",  "FPR9_BANK0", "FPR10_BANK0", "FPR11_BANK0", +        "FPR12_BANK0", "FPR13_BANK0", "FPR14_BANK0", "FPR15_BANK0", +         "FPR0_BANK1",  "FPR1_BANK1",  "FPR2_BANK1",  "FPR3_BANK1", +         "FPR4_BANK1",  "FPR5_BANK1",  "FPR6_BANK1",  "FPR7_BANK1", +         "FPR8_BANK1",  "FPR9_BANK1", "FPR10_BANK1", "FPR11_BANK1", +        "FPR12_BANK1", "FPR13_BANK1", "FPR14_BANK1", "FPR15_BANK1", +    }; + +    if (done_init) +        return; + +    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + +    for (i = 0; i < 24; i++) +        cpu_gregs[i] = tcg_global_mem_new_i32(TCG_AREG0, +                                              offsetof(CPUSH4State, gregs[i]), +                                              gregnames[i]); + +    cpu_pc = tcg_global_mem_new_i32(TCG_AREG0, +                                    offsetof(CPUSH4State, pc), "PC"); +    cpu_sr = tcg_global_mem_new_i32(TCG_AREG0, +                                    offsetof(CPUSH4State, sr), "SR"); +    cpu_sr_m = tcg_global_mem_new_i32(TCG_AREG0, +                                    offsetof(CPUSH4State, sr_m), "SR_M"); +    cpu_sr_q = tcg_global_mem_new_i32(TCG_AREG0, +                                    offsetof(CPUSH4State, sr_q), "SR_Q"); +    cpu_sr_t = tcg_global_mem_new_i32(TCG_AREG0, +                                    offsetof(CPUSH4State, sr_t), "SR_T"); +    cpu_ssr = tcg_global_mem_new_i32(TCG_AREG0, +                                     offsetof(CPUSH4State, ssr), "SSR"); +    cpu_spc = tcg_global_mem_new_i32(TCG_AREG0, +                                     offsetof(CPUSH4State, spc), "SPC"); +    cpu_gbr = tcg_global_mem_new_i32(TCG_AREG0, +                                     offsetof(CPUSH4State, gbr), "GBR"); +    cpu_vbr = tcg_global_mem_new_i32(TCG_AREG0, +                                     offsetof(CPUSH4State, vbr), "VBR"); +    cpu_sgr = tcg_global_mem_new_i32(TCG_AREG0, +                                     offsetof(CPUSH4State, sgr), "SGR"); +    cpu_dbr = tcg_global_mem_new_i32(TCG_AREG0, +                                     offsetof(CPUSH4State, dbr), "DBR"); +    cpu_mach = tcg_global_mem_new_i32(TCG_AREG0, +                                      offsetof(CPUSH4State, mach), "MACH"); +    cpu_macl = tcg_global_mem_new_i32(TCG_AREG0, +                                      offsetof(CPUSH4State, macl), "MACL"); +    cpu_pr = tcg_global_mem_new_i32(TCG_AREG0, +                                    offsetof(CPUSH4State, pr), "PR"); +    cpu_fpscr = tcg_global_mem_new_i32(TCG_AREG0, +                                       offsetof(CPUSH4State, fpscr), "FPSCR"); +    cpu_fpul = tcg_global_mem_new_i32(TCG_AREG0, +                                      offsetof(CPUSH4State, fpul), "FPUL"); + +    cpu_flags = tcg_global_mem_new_i32(TCG_AREG0, +				       offsetof(CPUSH4State, flags), "_flags_"); +    cpu_delayed_pc = tcg_global_mem_new_i32(TCG_AREG0, +					    offsetof(CPUSH4State, delayed_pc), +					    "_delayed_pc_"); +    cpu_ldst = tcg_global_mem_new_i32(TCG_AREG0, +				      offsetof(CPUSH4State, ldst), "_ldst_"); + +    for (i = 0; i < 32; i++) +        cpu_fregs[i] = tcg_global_mem_new_i32(TCG_AREG0, +                                              offsetof(CPUSH4State, fregs[i]), +                                              fregnames[i]); + +    done_init = 1; +} + +void superh_cpu_dump_state(CPUState *cs, FILE *f, +                           fprintf_function cpu_fprintf, int flags) +{ +    SuperHCPU *cpu = SUPERH_CPU(cs); +    CPUSH4State *env = &cpu->env; +    int i; +    cpu_fprintf(f, "pc=0x%08x sr=0x%08x pr=0x%08x fpscr=0x%08x\n", +                env->pc, cpu_read_sr(env), env->pr, env->fpscr); +    cpu_fprintf(f, "spc=0x%08x ssr=0x%08x gbr=0x%08x vbr=0x%08x\n", +		env->spc, env->ssr, env->gbr, env->vbr); +    cpu_fprintf(f, "sgr=0x%08x dbr=0x%08x delayed_pc=0x%08x fpul=0x%08x\n", +		env->sgr, env->dbr, env->delayed_pc, env->fpul); +    for (i = 0; i < 24; i += 4) { +	cpu_fprintf(f, "r%d=0x%08x r%d=0x%08x r%d=0x%08x r%d=0x%08x\n", +		    i, env->gregs[i], i + 1, env->gregs[i + 1], +		    i + 2, env->gregs[i + 2], i + 3, env->gregs[i + 3]); +    } +    if (env->flags & DELAY_SLOT) { +	cpu_fprintf(f, "in delay slot (delayed_pc=0x%08x)\n", +		    env->delayed_pc); +    } else if (env->flags & DELAY_SLOT_CONDITIONAL) { +	cpu_fprintf(f, "in conditional delay slot (delayed_pc=0x%08x)\n", +		    env->delayed_pc); +    } +} + +static void gen_read_sr(TCGv dst) +{ +    TCGv t0 = tcg_temp_new(); +    tcg_gen_shli_i32(t0, cpu_sr_q, SR_Q); +    tcg_gen_or_i32(dst, dst, t0); +    tcg_gen_shli_i32(t0, cpu_sr_m, SR_M); +    tcg_gen_or_i32(dst, dst, t0); +    tcg_gen_shli_i32(t0, cpu_sr_t, SR_T); +    tcg_gen_or_i32(dst, cpu_sr, t0); +    tcg_temp_free_i32(t0); +} + +static void gen_write_sr(TCGv src) +{ +    tcg_gen_andi_i32(cpu_sr, src, +                     ~((1u << SR_Q) | (1u << SR_M) | (1u << SR_T))); +    tcg_gen_shri_i32(cpu_sr_q, src, SR_Q); +    tcg_gen_andi_i32(cpu_sr_q, cpu_sr_q, 1); +    tcg_gen_shri_i32(cpu_sr_m, src, SR_M); +    tcg_gen_andi_i32(cpu_sr_m, cpu_sr_m, 1); +    tcg_gen_shri_i32(cpu_sr_t, src, SR_T); +    tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); +} + +static void gen_goto_tb(DisasContext * ctx, int n, target_ulong dest) +{ +    TranslationBlock *tb; +    tb = ctx->tb; + +    if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) && +	!ctx->singlestep_enabled) { +	/* Use a direct jump if in same page and singlestep not enabled */ +        tcg_gen_goto_tb(n); +        tcg_gen_movi_i32(cpu_pc, dest); +        tcg_gen_exit_tb((uintptr_t)tb + n); +    } else { +        tcg_gen_movi_i32(cpu_pc, dest); +        if (ctx->singlestep_enabled) +            gen_helper_debug(cpu_env); +        tcg_gen_exit_tb(0); +    } +} + +static void gen_jump(DisasContext * ctx) +{ +    if (ctx->delayed_pc == (uint32_t) - 1) { +	/* Target is not statically known, it comes necessarily from a +	   delayed jump as immediate jump are conditinal jumps */ +	tcg_gen_mov_i32(cpu_pc, cpu_delayed_pc); +	if (ctx->singlestep_enabled) +            gen_helper_debug(cpu_env); +	tcg_gen_exit_tb(0); +    } else { +	gen_goto_tb(ctx, 0, ctx->delayed_pc); +    } +} + +static inline void gen_branch_slot(uint32_t delayed_pc, int t) +{ +    TCGLabel *label = gen_new_label(); +    tcg_gen_movi_i32(cpu_delayed_pc, delayed_pc); +    tcg_gen_brcondi_i32(t ? TCG_COND_EQ : TCG_COND_NE, cpu_sr_t, 0, label); +    tcg_gen_ori_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE); +    gen_set_label(label); +} + +/* Immediate conditional jump (bt or bf) */ +static void gen_conditional_jump(DisasContext * ctx, +				 target_ulong ift, target_ulong ifnott) +{ +    TCGLabel *l1 = gen_new_label(); +    tcg_gen_brcondi_i32(TCG_COND_NE, cpu_sr_t, 0, l1); +    gen_goto_tb(ctx, 0, ifnott); +    gen_set_label(l1); +    gen_goto_tb(ctx, 1, ift); +} + +/* Delayed conditional jump (bt or bf) */ +static void gen_delayed_conditional_jump(DisasContext * ctx) +{ +    TCGLabel *l1; +    TCGv ds; + +    l1 = gen_new_label(); +    ds = tcg_temp_new(); +    tcg_gen_andi_i32(ds, cpu_flags, DELAY_SLOT_TRUE); +    tcg_gen_brcondi_i32(TCG_COND_NE, ds, 0, l1); +    gen_goto_tb(ctx, 1, ctx->pc + 2); +    gen_set_label(l1); +    tcg_gen_andi_i32(cpu_flags, cpu_flags, ~DELAY_SLOT_TRUE); +    gen_jump(ctx); +} + +static inline void gen_store_flags(uint32_t flags) +{ +    tcg_gen_andi_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE); +    tcg_gen_ori_i32(cpu_flags, cpu_flags, flags); +} + +static inline void gen_load_fpr64(TCGv_i64 t, int reg) +{ +    tcg_gen_concat_i32_i64(t, cpu_fregs[reg + 1], cpu_fregs[reg]); +} + +static inline void gen_store_fpr64 (TCGv_i64 t, int reg) +{ +    TCGv_i32 tmp = tcg_temp_new_i32(); +    tcg_gen_trunc_i64_i32(tmp, t); +    tcg_gen_mov_i32(cpu_fregs[reg + 1], tmp); +    tcg_gen_shri_i64(t, t, 32); +    tcg_gen_trunc_i64_i32(tmp, t); +    tcg_gen_mov_i32(cpu_fregs[reg], tmp); +    tcg_temp_free_i32(tmp); +} + +#define B3_0 (ctx->opcode & 0xf) +#define B6_4 ((ctx->opcode >> 4) & 0x7) +#define B7_4 ((ctx->opcode >> 4) & 0xf) +#define B7_0 (ctx->opcode & 0xff) +#define B7_0s ((int32_t) (int8_t) (ctx->opcode & 0xff)) +#define B11_0s (ctx->opcode & 0x800 ? 0xfffff000 | (ctx->opcode & 0xfff) : \ +  (ctx->opcode & 0xfff)) +#define B11_8 ((ctx->opcode >> 8) & 0xf) +#define B15_12 ((ctx->opcode >> 12) & 0xf) + +#define REG(x) ((x) < 8 && (ctx->flags & (1u << SR_MD))\ +                        && (ctx->flags & (1u << SR_RB))\ +                ? (cpu_gregs[x + 16]) : (cpu_gregs[x])) + +#define ALTREG(x) ((x) < 8 && (!(ctx->flags & (1u << SR_MD))\ +                               || !(ctx->flags & (1u << SR_RB)))\ +		? (cpu_gregs[x + 16]) : (cpu_gregs[x])) + +#define FREG(x) (ctx->flags & FPSCR_FR ? (x) ^ 0x10 : (x)) +#define XHACK(x) ((((x) & 1 ) << 4) | ((x) & 0xe)) +#define XREG(x) (ctx->flags & FPSCR_FR ? XHACK(x) ^ 0x10 : XHACK(x)) +#define DREG(x) FREG(x) /* Assumes lsb of (x) is always 0 */ + +#define CHECK_NOT_DELAY_SLOT \ +  if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL))     \ +  {                                                           \ +      tcg_gen_movi_i32(cpu_pc, ctx->pc);                      \ +      gen_helper_raise_slot_illegal_instruction(cpu_env);     \ +      ctx->bstate = BS_BRANCH;                                \ +      return;                                                 \ +  } + +#define CHECK_PRIVILEGED                                        \ +  if (IS_USER(ctx)) {                                           \ +      tcg_gen_movi_i32(cpu_pc, ctx->pc);                        \ +      if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \ +          gen_helper_raise_slot_illegal_instruction(cpu_env);   \ +      } else {                                                  \ +          gen_helper_raise_illegal_instruction(cpu_env);        \ +      }                                                         \ +      ctx->bstate = BS_BRANCH;                                  \ +      return;                                                   \ +  } + +#define CHECK_FPU_ENABLED                                       \ +  if (ctx->flags & (1u << SR_FD)) {                             \ +      tcg_gen_movi_i32(cpu_pc, ctx->pc);                        \ +      if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \ +          gen_helper_raise_slot_fpu_disable(cpu_env);           \ +      } else {                                                  \ +          gen_helper_raise_fpu_disable(cpu_env);                \ +      }                                                         \ +      ctx->bstate = BS_BRANCH;                                  \ +      return;                                                   \ +  } + +static void _decode_opc(DisasContext * ctx) +{ +    /* This code tries to make movcal emulation sufficiently +       accurate for Linux purposes.  This instruction writes +       memory, and prior to that, always allocates a cache line. +       It is used in two contexts: +       - in memcpy, where data is copied in blocks, the first write +       of to a block uses movca.l for performance. +       - in arch/sh/mm/cache-sh4.c, movcal.l + ocbi combination is used +       to flush the cache. Here, the data written by movcal.l is never +       written to memory, and the data written is just bogus. + +       To simulate this, we simulate movcal.l, we store the value to memory, +       but we also remember the previous content. If we see ocbi, we check +       if movcal.l for that address was done previously. If so, the write should +       not have hit the memory, so we restore the previous content. +       When we see an instruction that is neither movca.l +       nor ocbi, the previous content is discarded. + +       To optimize, we only try to flush stores when we're at the start of +       TB, or if we already saw movca.l in this TB and did not flush stores +       yet.  */ +    if (ctx->has_movcal) +	{ +	  int opcode = ctx->opcode & 0xf0ff; +	  if (opcode != 0x0093 /* ocbi */ +	      && opcode != 0x00c3 /* movca.l */) +	      { +                  gen_helper_discard_movcal_backup(cpu_env); +		  ctx->has_movcal = 0; +	      } +	} + +#if 0 +    fprintf(stderr, "Translating opcode 0x%04x\n", ctx->opcode); +#endif + +    switch (ctx->opcode) { +    case 0x0019:		/* div0u */ +        tcg_gen_movi_i32(cpu_sr_m, 0); +        tcg_gen_movi_i32(cpu_sr_q, 0); +        tcg_gen_movi_i32(cpu_sr_t, 0); +	return; +    case 0x000b:		/* rts */ +	CHECK_NOT_DELAY_SLOT +	tcg_gen_mov_i32(cpu_delayed_pc, cpu_pr); +	ctx->flags |= DELAY_SLOT; +	ctx->delayed_pc = (uint32_t) - 1; +	return; +    case 0x0028:		/* clrmac */ +	tcg_gen_movi_i32(cpu_mach, 0); +	tcg_gen_movi_i32(cpu_macl, 0); +	return; +    case 0x0048:		/* clrs */ +        tcg_gen_andi_i32(cpu_sr, cpu_sr, ~(1u << SR_S)); +	return; +    case 0x0008:		/* clrt */ +        tcg_gen_movi_i32(cpu_sr_t, 0); +	return; +    case 0x0038:		/* ldtlb */ +	CHECK_PRIVILEGED +        gen_helper_ldtlb(cpu_env); +	return; +    case 0x002b:		/* rte */ +	CHECK_PRIVILEGED +	CHECK_NOT_DELAY_SLOT +        gen_write_sr(cpu_ssr); +	tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc); +	ctx->flags |= DELAY_SLOT; +	ctx->delayed_pc = (uint32_t) - 1; +	return; +    case 0x0058:		/* sets */ +        tcg_gen_ori_i32(cpu_sr, cpu_sr, (1u << SR_S)); +	return; +    case 0x0018:		/* sett */ +        tcg_gen_movi_i32(cpu_sr_t, 1); +	return; +    case 0xfbfd:		/* frchg */ +	tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_FR); +	ctx->bstate = BS_STOP; +	return; +    case 0xf3fd:		/* fschg */ +        tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_SZ); +	ctx->bstate = BS_STOP; +	return; +    case 0x0009:		/* nop */ +	return; +    case 0x001b:		/* sleep */ +	CHECK_PRIVILEGED +        tcg_gen_movi_i32(cpu_pc, ctx->pc + 2); +        gen_helper_sleep(cpu_env); +	return; +    } + +    switch (ctx->opcode & 0xf000) { +    case 0x1000:		/* mov.l Rm,@(disp,Rn) */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, REG(B11_8), B3_0 * 4); +            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL); +	    tcg_temp_free(addr); +	} +	return; +    case 0x5000:		/* mov.l @(disp,Rm),Rn */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 4); +            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL); +	    tcg_temp_free(addr); +	} +	return; +    case 0xe000:		/* mov #imm,Rn */ +	tcg_gen_movi_i32(REG(B11_8), B7_0s); +	return; +    case 0x9000:		/* mov.w @(disp,PC),Rn */ +	{ +	    TCGv addr = tcg_const_i32(ctx->pc + 4 + B7_0 * 2); +            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESW); +	    tcg_temp_free(addr); +	} +	return; +    case 0xd000:		/* mov.l @(disp,PC),Rn */ +	{ +	    TCGv addr = tcg_const_i32((ctx->pc + 4 + B7_0 * 4) & ~3); +            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL); +	    tcg_temp_free(addr); +	} +	return; +    case 0x7000:		/* add #imm,Rn */ +	tcg_gen_addi_i32(REG(B11_8), REG(B11_8), B7_0s); +	return; +    case 0xa000:		/* bra disp */ +	CHECK_NOT_DELAY_SLOT +	ctx->delayed_pc = ctx->pc + 4 + B11_0s * 2; +	tcg_gen_movi_i32(cpu_delayed_pc, ctx->delayed_pc); +	ctx->flags |= DELAY_SLOT; +	return; +    case 0xb000:		/* bsr disp */ +	CHECK_NOT_DELAY_SLOT +	tcg_gen_movi_i32(cpu_pr, ctx->pc + 4); +	ctx->delayed_pc = ctx->pc + 4 + B11_0s * 2; +	tcg_gen_movi_i32(cpu_delayed_pc, ctx->delayed_pc); +	ctx->flags |= DELAY_SLOT; +	return; +    } + +    switch (ctx->opcode & 0xf00f) { +    case 0x6003:		/* mov Rm,Rn */ +	tcg_gen_mov_i32(REG(B11_8), REG(B7_4)); +	return; +    case 0x2000:		/* mov.b Rm,@Rn */ +        tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_UB); +	return; +    case 0x2001:		/* mov.w Rm,@Rn */ +        tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_TEUW); +	return; +    case 0x2002:		/* mov.l Rm,@Rn */ +        tcg_gen_qemu_st_i32(REG(B7_4), REG(B11_8), ctx->memidx, MO_TEUL); +	return; +    case 0x6000:		/* mov.b @Rm,Rn */ +        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_SB); +	return; +    case 0x6001:		/* mov.w @Rm,Rn */ +        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESW); +	return; +    case 0x6002:		/* mov.l @Rm,Rn */ +        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESL); +	return; +    case 0x2004:		/* mov.b Rm,@-Rn */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_subi_i32(addr, REG(B11_8), 1); +            /* might cause re-execution */ +            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_UB); +	    tcg_gen_mov_i32(REG(B11_8), addr);			/* modify register status */ +	    tcg_temp_free(addr); +	} +	return; +    case 0x2005:		/* mov.w Rm,@-Rn */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_subi_i32(addr, REG(B11_8), 2); +            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUW); +	    tcg_gen_mov_i32(REG(B11_8), addr); +	    tcg_temp_free(addr); +	} +	return; +    case 0x2006:		/* mov.l Rm,@-Rn */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_subi_i32(addr, REG(B11_8), 4); +            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL); +	    tcg_gen_mov_i32(REG(B11_8), addr); +	} +	return; +    case 0x6004:		/* mov.b @Rm+,Rn */ +        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_SB); +	if ( B11_8 != B7_4 ) +		tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 1); +	return; +    case 0x6005:		/* mov.w @Rm+,Rn */ +        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESW); +	if ( B11_8 != B7_4 ) +		tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2); +	return; +    case 0x6006:		/* mov.l @Rm+,Rn */ +        tcg_gen_qemu_ld_i32(REG(B11_8), REG(B7_4), ctx->memidx, MO_TESL); +	if ( B11_8 != B7_4 ) +		tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4); +	return; +    case 0x0004:		/* mov.b Rm,@(R0,Rn) */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_add_i32(addr, REG(B11_8), REG(0)); +            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_UB); +	    tcg_temp_free(addr); +	} +	return; +    case 0x0005:		/* mov.w Rm,@(R0,Rn) */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_add_i32(addr, REG(B11_8), REG(0)); +            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUW); +	    tcg_temp_free(addr); +	} +	return; +    case 0x0006:		/* mov.l Rm,@(R0,Rn) */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_add_i32(addr, REG(B11_8), REG(0)); +            tcg_gen_qemu_st_i32(REG(B7_4), addr, ctx->memidx, MO_TEUL); +	    tcg_temp_free(addr); +	} +	return; +    case 0x000c:		/* mov.b @(R0,Rm),Rn */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_add_i32(addr, REG(B7_4), REG(0)); +            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_SB); +	    tcg_temp_free(addr); +	} +	return; +    case 0x000d:		/* mov.w @(R0,Rm),Rn */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_add_i32(addr, REG(B7_4), REG(0)); +            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESW); +	    tcg_temp_free(addr); +	} +	return; +    case 0x000e:		/* mov.l @(R0,Rm),Rn */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_add_i32(addr, REG(B7_4), REG(0)); +            tcg_gen_qemu_ld_i32(REG(B11_8), addr, ctx->memidx, MO_TESL); +	    tcg_temp_free(addr); +	} +	return; +    case 0x6008:		/* swap.b Rm,Rn */ +	{ +	    TCGv high, low; +	    high = tcg_temp_new(); +	    tcg_gen_andi_i32(high, REG(B7_4), 0xffff0000); +	    low = tcg_temp_new(); +	    tcg_gen_ext16u_i32(low, REG(B7_4)); +	    tcg_gen_bswap16_i32(low, low); +	    tcg_gen_or_i32(REG(B11_8), high, low); +	    tcg_temp_free(low); +	    tcg_temp_free(high); +	} +	return; +    case 0x6009:		/* swap.w Rm,Rn */ +        tcg_gen_rotli_i32(REG(B11_8), REG(B7_4), 16); +	return; +    case 0x200d:		/* xtrct Rm,Rn */ +	{ +	    TCGv high, low; +	    high = tcg_temp_new(); +	    tcg_gen_shli_i32(high, REG(B7_4), 16); +	    low = tcg_temp_new(); +	    tcg_gen_shri_i32(low, REG(B11_8), 16); +	    tcg_gen_or_i32(REG(B11_8), high, low); +	    tcg_temp_free(low); +	    tcg_temp_free(high); +	} +	return; +    case 0x300c:		/* add Rm,Rn */ +	tcg_gen_add_i32(REG(B11_8), REG(B11_8), REG(B7_4)); +	return; +    case 0x300e:		/* addc Rm,Rn */ +        { +            TCGv t0, t1; +            t0 = tcg_const_tl(0); +            t1 = tcg_temp_new(); +            tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0); +            tcg_gen_add2_i32(REG(B11_8), cpu_sr_t, +                             REG(B11_8), t0, t1, cpu_sr_t); +            tcg_temp_free(t0); +            tcg_temp_free(t1); +        } +	return; +    case 0x300f:		/* addv Rm,Rn */ +        { +            TCGv t0, t1, t2; +            t0 = tcg_temp_new(); +            tcg_gen_add_i32(t0, REG(B7_4), REG(B11_8)); +            t1 = tcg_temp_new(); +            tcg_gen_xor_i32(t1, t0, REG(B11_8)); +            t2 = tcg_temp_new(); +            tcg_gen_xor_i32(t2, REG(B7_4), REG(B11_8)); +            tcg_gen_andc_i32(cpu_sr_t, t1, t2); +            tcg_temp_free(t2); +            tcg_gen_shri_i32(cpu_sr_t, cpu_sr_t, 31); +            tcg_temp_free(t1); +            tcg_gen_mov_i32(REG(B7_4), t0); +            tcg_temp_free(t0); +        } +	return; +    case 0x2009:		/* and Rm,Rn */ +	tcg_gen_and_i32(REG(B11_8), REG(B11_8), REG(B7_4)); +	return; +    case 0x3000:		/* cmp/eq Rm,Rn */ +        tcg_gen_setcond_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), REG(B7_4)); +	return; +    case 0x3003:		/* cmp/ge Rm,Rn */ +        tcg_gen_setcond_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), REG(B7_4)); +	return; +    case 0x3007:		/* cmp/gt Rm,Rn */ +        tcg_gen_setcond_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), REG(B7_4)); +	return; +    case 0x3006:		/* cmp/hi Rm,Rn */ +        tcg_gen_setcond_i32(TCG_COND_GTU, cpu_sr_t, REG(B11_8), REG(B7_4)); +	return; +    case 0x3002:		/* cmp/hs Rm,Rn */ +        tcg_gen_setcond_i32(TCG_COND_GEU, cpu_sr_t, REG(B11_8), REG(B7_4)); +	return; +    case 0x200c:		/* cmp/str Rm,Rn */ +	{ +	    TCGv cmp1 = tcg_temp_new(); +	    TCGv cmp2 = tcg_temp_new(); +	    tcg_gen_xor_i32(cmp1, REG(B7_4), REG(B11_8)); +	    tcg_gen_andi_i32(cmp2, cmp1, 0xff000000); +            tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, cmp2, 0); +	    tcg_gen_andi_i32(cmp2, cmp1, 0x00ff0000); +	    tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); +            tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); +	    tcg_gen_andi_i32(cmp2, cmp1, 0x0000ff00); +	    tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); +            tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); +	    tcg_gen_andi_i32(cmp2, cmp1, 0x000000ff); +	    tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); +            tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); +	    tcg_temp_free(cmp2); +	    tcg_temp_free(cmp1); +	} +	return; +    case 0x2007:		/* div0s Rm,Rn */ +        tcg_gen_shri_i32(cpu_sr_q, REG(B11_8), 31);         /* SR_Q */ +        tcg_gen_shri_i32(cpu_sr_m, REG(B7_4), 31);          /* SR_M */ +        tcg_gen_xor_i32(cpu_sr_t, cpu_sr_q, cpu_sr_m);      /* SR_T */ +	return; +    case 0x3004:		/* div1 Rm,Rn */ +        { +            TCGv t0 = tcg_temp_new(); +            TCGv t1 = tcg_temp_new(); +            TCGv t2 = tcg_temp_new(); +            TCGv zero = tcg_const_i32(0); + +            /* shift left arg1, saving the bit being pushed out and inserting +               T on the right */ +            tcg_gen_shri_i32(t0, REG(B11_8), 31); +            tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); +            tcg_gen_or_i32(REG(B11_8), REG(B11_8), cpu_sr_t); + +            /* Add or subtract arg0 from arg1 depending if Q == M. To avoid +               using 64-bit temps, we compute arg0's high part from q ^ m, so +               that it is 0x00000000 when adding the value or 0xffffffff when +               subtracting it. */ +            tcg_gen_xor_i32(t1, cpu_sr_q, cpu_sr_m); +            tcg_gen_subi_i32(t1, t1, 1); +            tcg_gen_neg_i32(t2, REG(B7_4)); +            tcg_gen_movcond_i32(TCG_COND_EQ, t2, t1, zero, REG(B7_4), t2); +            tcg_gen_add2_i32(REG(B11_8), t1, REG(B11_8), zero, t2, t1); + +            /* compute T and Q depending on carry */ +            tcg_gen_andi_i32(t1, t1, 1); +            tcg_gen_xor_i32(t1, t1, t0); +            tcg_gen_xori_i32(cpu_sr_t, t1, 1); +            tcg_gen_xor_i32(cpu_sr_q, cpu_sr_m, t1); + +            tcg_temp_free(zero); +            tcg_temp_free(t2); +            tcg_temp_free(t1); +            tcg_temp_free(t0); +        } +	return; +    case 0x300d:		/* dmuls.l Rm,Rn */ +        tcg_gen_muls2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8)); +	return; +    case 0x3005:		/* dmulu.l Rm,Rn */ +        tcg_gen_mulu2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8)); +	return; +    case 0x600e:		/* exts.b Rm,Rn */ +	tcg_gen_ext8s_i32(REG(B11_8), REG(B7_4)); +	return; +    case 0x600f:		/* exts.w Rm,Rn */ +	tcg_gen_ext16s_i32(REG(B11_8), REG(B7_4)); +	return; +    case 0x600c:		/* extu.b Rm,Rn */ +	tcg_gen_ext8u_i32(REG(B11_8), REG(B7_4)); +	return; +    case 0x600d:		/* extu.w Rm,Rn */ +	tcg_gen_ext16u_i32(REG(B11_8), REG(B7_4)); +	return; +    case 0x000f:		/* mac.l @Rm+,@Rn+ */ +	{ +	    TCGv arg0, arg1; +	    arg0 = tcg_temp_new(); +            tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, MO_TESL); +	    arg1 = tcg_temp_new(); +            tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, MO_TESL); +            gen_helper_macl(cpu_env, arg0, arg1); +	    tcg_temp_free(arg1); +	    tcg_temp_free(arg0); +	    tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4); +	    tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); +	} +	return; +    case 0x400f:		/* mac.w @Rm+,@Rn+ */ +	{ +	    TCGv arg0, arg1; +	    arg0 = tcg_temp_new(); +            tcg_gen_qemu_ld_i32(arg0, REG(B7_4), ctx->memidx, MO_TESL); +	    arg1 = tcg_temp_new(); +            tcg_gen_qemu_ld_i32(arg1, REG(B11_8), ctx->memidx, MO_TESL); +            gen_helper_macw(cpu_env, arg0, arg1); +	    tcg_temp_free(arg1); +	    tcg_temp_free(arg0); +	    tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 2); +	    tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 2); +	} +	return; +    case 0x0007:		/* mul.l Rm,Rn */ +	tcg_gen_mul_i32(cpu_macl, REG(B7_4), REG(B11_8)); +	return; +    case 0x200f:		/* muls.w Rm,Rn */ +	{ +	    TCGv arg0, arg1; +	    arg0 = tcg_temp_new(); +	    tcg_gen_ext16s_i32(arg0, REG(B7_4)); +	    arg1 = tcg_temp_new(); +	    tcg_gen_ext16s_i32(arg1, REG(B11_8)); +	    tcg_gen_mul_i32(cpu_macl, arg0, arg1); +	    tcg_temp_free(arg1); +	    tcg_temp_free(arg0); +	} +	return; +    case 0x200e:		/* mulu.w Rm,Rn */ +	{ +	    TCGv arg0, arg1; +	    arg0 = tcg_temp_new(); +	    tcg_gen_ext16u_i32(arg0, REG(B7_4)); +	    arg1 = tcg_temp_new(); +	    tcg_gen_ext16u_i32(arg1, REG(B11_8)); +	    tcg_gen_mul_i32(cpu_macl, arg0, arg1); +	    tcg_temp_free(arg1); +	    tcg_temp_free(arg0); +	} +	return; +    case 0x600b:		/* neg Rm,Rn */ +	tcg_gen_neg_i32(REG(B11_8), REG(B7_4)); +	return; +    case 0x600a:		/* negc Rm,Rn */ +        { +            TCGv t0 = tcg_const_i32(0); +            tcg_gen_add2_i32(REG(B11_8), cpu_sr_t, +                             REG(B7_4), t0, cpu_sr_t, t0); +            tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t, +                             t0, t0, REG(B11_8), cpu_sr_t); +            tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); +            tcg_temp_free(t0); +        } +	return; +    case 0x6007:		/* not Rm,Rn */ +	tcg_gen_not_i32(REG(B11_8), REG(B7_4)); +	return; +    case 0x200b:		/* or Rm,Rn */ +	tcg_gen_or_i32(REG(B11_8), REG(B11_8), REG(B7_4)); +	return; +    case 0x400c:		/* shad Rm,Rn */ +	{ +            TCGLabel *label1 = gen_new_label(); +            TCGLabel *label2 = gen_new_label(); +            TCGLabel *label3 = gen_new_label(); +            TCGLabel *label4 = gen_new_label(); +	    TCGv shift; +	    tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1); +	    /* Rm positive, shift to the left */ +            shift = tcg_temp_new(); +	    tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); +	    tcg_gen_shl_i32(REG(B11_8), REG(B11_8), shift); +	    tcg_temp_free(shift); +	    tcg_gen_br(label4); +	    /* Rm negative, shift to the right */ +	    gen_set_label(label1); +            shift = tcg_temp_new(); +	    tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); +	    tcg_gen_brcondi_i32(TCG_COND_EQ, shift, 0, label2); +	    tcg_gen_not_i32(shift, REG(B7_4)); +	    tcg_gen_andi_i32(shift, shift, 0x1f); +	    tcg_gen_addi_i32(shift, shift, 1); +	    tcg_gen_sar_i32(REG(B11_8), REG(B11_8), shift); +	    tcg_temp_free(shift); +	    tcg_gen_br(label4); +	    /* Rm = -32 */ +	    gen_set_label(label2); +	    tcg_gen_brcondi_i32(TCG_COND_LT, REG(B11_8), 0, label3); +	    tcg_gen_movi_i32(REG(B11_8), 0); +	    tcg_gen_br(label4); +	    gen_set_label(label3); +	    tcg_gen_movi_i32(REG(B11_8), 0xffffffff); +	    gen_set_label(label4); +	} +	return; +    case 0x400d:		/* shld Rm,Rn */ +	{ +            TCGLabel *label1 = gen_new_label(); +            TCGLabel *label2 = gen_new_label(); +            TCGLabel *label3 = gen_new_label(); +	    TCGv shift; +	    tcg_gen_brcondi_i32(TCG_COND_LT, REG(B7_4), 0, label1); +	    /* Rm positive, shift to the left */ +            shift = tcg_temp_new(); +	    tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); +	    tcg_gen_shl_i32(REG(B11_8), REG(B11_8), shift); +	    tcg_temp_free(shift); +	    tcg_gen_br(label3); +	    /* Rm negative, shift to the right */ +	    gen_set_label(label1); +            shift = tcg_temp_new(); +	    tcg_gen_andi_i32(shift, REG(B7_4), 0x1f); +	    tcg_gen_brcondi_i32(TCG_COND_EQ, shift, 0, label2); +	    tcg_gen_not_i32(shift, REG(B7_4)); +	    tcg_gen_andi_i32(shift, shift, 0x1f); +	    tcg_gen_addi_i32(shift, shift, 1); +	    tcg_gen_shr_i32(REG(B11_8), REG(B11_8), shift); +	    tcg_temp_free(shift); +	    tcg_gen_br(label3); +	    /* Rm = -32 */ +	    gen_set_label(label2); +	    tcg_gen_movi_i32(REG(B11_8), 0); +	    gen_set_label(label3); +	} +	return; +    case 0x3008:		/* sub Rm,Rn */ +	tcg_gen_sub_i32(REG(B11_8), REG(B11_8), REG(B7_4)); +	return; +    case 0x300a:		/* subc Rm,Rn */ +        { +            TCGv t0, t1; +            t0 = tcg_const_tl(0); +            t1 = tcg_temp_new(); +            tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0); +            tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t, +                             REG(B11_8), t0, t1, cpu_sr_t); +            tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); +            tcg_temp_free(t0); +            tcg_temp_free(t1); +        } +	return; +    case 0x300b:		/* subv Rm,Rn */ +        { +            TCGv t0, t1, t2; +            t0 = tcg_temp_new(); +            tcg_gen_sub_i32(t0, REG(B11_8), REG(B7_4)); +            t1 = tcg_temp_new(); +            tcg_gen_xor_i32(t1, t0, REG(B7_4)); +            t2 = tcg_temp_new(); +            tcg_gen_xor_i32(t2, REG(B11_8), REG(B7_4)); +            tcg_gen_and_i32(t1, t1, t2); +            tcg_temp_free(t2); +            tcg_gen_shri_i32(cpu_sr_t, t1, 31); +            tcg_temp_free(t1); +            tcg_gen_mov_i32(REG(B11_8), t0); +            tcg_temp_free(t0); +        } +	return; +    case 0x2008:		/* tst Rm,Rn */ +	{ +	    TCGv val = tcg_temp_new(); +	    tcg_gen_and_i32(val, REG(B7_4), REG(B11_8)); +            tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); +	    tcg_temp_free(val); +	} +	return; +    case 0x200a:		/* xor Rm,Rn */ +	tcg_gen_xor_i32(REG(B11_8), REG(B11_8), REG(B7_4)); +	return; +    case 0xf00c: /* fmov {F,D,X}Rm,{F,D,X}Rn - FPSCR: Nothing */ +	CHECK_FPU_ENABLED +        if (ctx->flags & FPSCR_SZ) { +	    TCGv_i64 fp = tcg_temp_new_i64(); +	    gen_load_fpr64(fp, XREG(B7_4)); +	    gen_store_fpr64(fp, XREG(B11_8)); +	    tcg_temp_free_i64(fp); +	} else { +	    tcg_gen_mov_i32(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B7_4)]); +	} +	return; +    case 0xf00a: /* fmov {F,D,X}Rm,@Rn - FPSCR: Nothing */ +	CHECK_FPU_ENABLED +        if (ctx->flags & FPSCR_SZ) { +	    TCGv addr_hi = tcg_temp_new(); +	    int fr = XREG(B7_4); +	    tcg_gen_addi_i32(addr_hi, REG(B11_8), 4); +            tcg_gen_qemu_st_i32(cpu_fregs[fr], REG(B11_8), +                                ctx->memidx, MO_TEUL); +            tcg_gen_qemu_st_i32(cpu_fregs[fr+1], addr_hi, +                                ctx->memidx, MO_TEUL); +	    tcg_temp_free(addr_hi); +	} else { +            tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], REG(B11_8), +                                ctx->memidx, MO_TEUL); +	} +	return; +    case 0xf008: /* fmov @Rm,{F,D,X}Rn - FPSCR: Nothing */ +	CHECK_FPU_ENABLED +        if (ctx->flags & FPSCR_SZ) { +	    TCGv addr_hi = tcg_temp_new(); +	    int fr = XREG(B11_8); +	    tcg_gen_addi_i32(addr_hi, REG(B7_4), 4); +            tcg_gen_qemu_ld_i32(cpu_fregs[fr], REG(B7_4), ctx->memidx, MO_TEUL); +            tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr_hi, ctx->memidx, MO_TEUL); +	    tcg_temp_free(addr_hi); +	} else { +            tcg_gen_qemu_ld_i32(cpu_fregs[FREG(B11_8)], REG(B7_4), +                                ctx->memidx, MO_TEUL); +	} +	return; +    case 0xf009: /* fmov @Rm+,{F,D,X}Rn - FPSCR: Nothing */ +	CHECK_FPU_ENABLED +        if (ctx->flags & FPSCR_SZ) { +	    TCGv addr_hi = tcg_temp_new(); +	    int fr = XREG(B11_8); +	    tcg_gen_addi_i32(addr_hi, REG(B7_4), 4); +            tcg_gen_qemu_ld_i32(cpu_fregs[fr], REG(B7_4), ctx->memidx, MO_TEUL); +            tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr_hi, ctx->memidx, MO_TEUL); +	    tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 8); +	    tcg_temp_free(addr_hi); +	} else { +            tcg_gen_qemu_ld_i32(cpu_fregs[FREG(B11_8)], REG(B7_4), +                                ctx->memidx, MO_TEUL); +	    tcg_gen_addi_i32(REG(B7_4), REG(B7_4), 4); +	} +	return; +    case 0xf00b: /* fmov {F,D,X}Rm,@-Rn - FPSCR: Nothing */ +	CHECK_FPU_ENABLED +        TCGv addr = tcg_temp_new_i32(); +        tcg_gen_subi_i32(addr, REG(B11_8), 4); +        if (ctx->flags & FPSCR_SZ) { +	    int fr = XREG(B7_4); +            tcg_gen_qemu_st_i32(cpu_fregs[fr+1], addr, ctx->memidx, MO_TEUL); +	    tcg_gen_subi_i32(addr, addr, 4); +            tcg_gen_qemu_st_i32(cpu_fregs[fr], addr, ctx->memidx, MO_TEUL); +	} else { +            tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], addr, +                                ctx->memidx, MO_TEUL); +	} +        tcg_gen_mov_i32(REG(B11_8), addr); +        tcg_temp_free(addr); +	return; +    case 0xf006: /* fmov @(R0,Rm),{F,D,X}Rm - FPSCR: Nothing */ +	CHECK_FPU_ENABLED +	{ +	    TCGv addr = tcg_temp_new_i32(); +	    tcg_gen_add_i32(addr, REG(B7_4), REG(0)); +            if (ctx->flags & FPSCR_SZ) { +		int fr = XREG(B11_8); +                tcg_gen_qemu_ld_i32(cpu_fregs[fr], addr, +                                    ctx->memidx, MO_TEUL); +		tcg_gen_addi_i32(addr, addr, 4); +                tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr, +                                    ctx->memidx, MO_TEUL); +	    } else { +                tcg_gen_qemu_ld_i32(cpu_fregs[FREG(B11_8)], addr, +                                    ctx->memidx, MO_TEUL); +	    } +	    tcg_temp_free(addr); +	} +	return; +    case 0xf007: /* fmov {F,D,X}Rn,@(R0,Rn) - FPSCR: Nothing */ +	CHECK_FPU_ENABLED +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_add_i32(addr, REG(B11_8), REG(0)); +            if (ctx->flags & FPSCR_SZ) { +		int fr = XREG(B7_4); +                tcg_gen_qemu_ld_i32(cpu_fregs[fr], addr, +                                    ctx->memidx, MO_TEUL); +		tcg_gen_addi_i32(addr, addr, 4); +                tcg_gen_qemu_ld_i32(cpu_fregs[fr+1], addr, +                                    ctx->memidx, MO_TEUL); +	    } else { +                tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], addr, +                                    ctx->memidx, MO_TEUL); +	    } +	    tcg_temp_free(addr); +	} +	return; +    case 0xf000: /* fadd Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */ +    case 0xf001: /* fsub Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */ +    case 0xf002: /* fmul Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */ +    case 0xf003: /* fdiv Rm,Rn - FPSCR: R[PR,Enable.O/U/I]/W[Cause,Flag] */ +    case 0xf004: /* fcmp/eq Rm,Rn - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */ +    case 0xf005: /* fcmp/gt Rm,Rn - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */ +	{ +	    CHECK_FPU_ENABLED +            if (ctx->flags & FPSCR_PR) { +                TCGv_i64 fp0, fp1; + +		if (ctx->opcode & 0x0110) +		    break; /* illegal instruction */ +		fp0 = tcg_temp_new_i64(); +		fp1 = tcg_temp_new_i64(); +		gen_load_fpr64(fp0, DREG(B11_8)); +		gen_load_fpr64(fp1, DREG(B7_4)); +                switch (ctx->opcode & 0xf00f) { +                case 0xf000:		/* fadd Rm,Rn */ +                    gen_helper_fadd_DT(fp0, cpu_env, fp0, fp1); +                    break; +                case 0xf001:		/* fsub Rm,Rn */ +                    gen_helper_fsub_DT(fp0, cpu_env, fp0, fp1); +                    break; +                case 0xf002:		/* fmul Rm,Rn */ +                    gen_helper_fmul_DT(fp0, cpu_env, fp0, fp1); +                    break; +                case 0xf003:		/* fdiv Rm,Rn */ +                    gen_helper_fdiv_DT(fp0, cpu_env, fp0, fp1); +                    break; +                case 0xf004:		/* fcmp/eq Rm,Rn */ +                    gen_helper_fcmp_eq_DT(cpu_env, fp0, fp1); +                    return; +                case 0xf005:		/* fcmp/gt Rm,Rn */ +                    gen_helper_fcmp_gt_DT(cpu_env, fp0, fp1); +                    return; +                } +		gen_store_fpr64(fp0, DREG(B11_8)); +                tcg_temp_free_i64(fp0); +                tcg_temp_free_i64(fp1); +	    } else { +                switch (ctx->opcode & 0xf00f) { +                case 0xf000:		/* fadd Rm,Rn */ +                    gen_helper_fadd_FT(cpu_fregs[FREG(B11_8)], cpu_env, +                                       cpu_fregs[FREG(B11_8)], +                                       cpu_fregs[FREG(B7_4)]); +                    break; +                case 0xf001:		/* fsub Rm,Rn */ +                    gen_helper_fsub_FT(cpu_fregs[FREG(B11_8)], cpu_env, +                                       cpu_fregs[FREG(B11_8)], +                                       cpu_fregs[FREG(B7_4)]); +                    break; +                case 0xf002:		/* fmul Rm,Rn */ +                    gen_helper_fmul_FT(cpu_fregs[FREG(B11_8)], cpu_env, +                                       cpu_fregs[FREG(B11_8)], +                                       cpu_fregs[FREG(B7_4)]); +                    break; +                case 0xf003:		/* fdiv Rm,Rn */ +                    gen_helper_fdiv_FT(cpu_fregs[FREG(B11_8)], cpu_env, +                                       cpu_fregs[FREG(B11_8)], +                                       cpu_fregs[FREG(B7_4)]); +                    break; +                case 0xf004:		/* fcmp/eq Rm,Rn */ +                    gen_helper_fcmp_eq_FT(cpu_env, cpu_fregs[FREG(B11_8)], +                                          cpu_fregs[FREG(B7_4)]); +                    return; +                case 0xf005:		/* fcmp/gt Rm,Rn */ +                    gen_helper_fcmp_gt_FT(cpu_env, cpu_fregs[FREG(B11_8)], +                                          cpu_fregs[FREG(B7_4)]); +                    return; +                } +	    } +	} +	return; +    case 0xf00e: /* fmac FR0,RM,Rn */ +        { +            CHECK_FPU_ENABLED +            if (ctx->flags & FPSCR_PR) { +                break; /* illegal instruction */ +            } else { +                gen_helper_fmac_FT(cpu_fregs[FREG(B11_8)], cpu_env, +                                   cpu_fregs[FREG(0)], cpu_fregs[FREG(B7_4)], +                                   cpu_fregs[FREG(B11_8)]); +                return; +            } +        } +    } + +    switch (ctx->opcode & 0xff00) { +    case 0xc900:		/* and #imm,R0 */ +	tcg_gen_andi_i32(REG(0), REG(0), B7_0); +	return; +    case 0xcd00:		/* and.b #imm,@(R0,GBR) */ +	{ +	    TCGv addr, val; +	    addr = tcg_temp_new(); +	    tcg_gen_add_i32(addr, REG(0), cpu_gbr); +	    val = tcg_temp_new(); +            tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB); +	    tcg_gen_andi_i32(val, val, B7_0); +            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB); +	    tcg_temp_free(val); +	    tcg_temp_free(addr); +	} +	return; +    case 0x8b00:		/* bf label */ +	CHECK_NOT_DELAY_SLOT +	    gen_conditional_jump(ctx, ctx->pc + 2, +				 ctx->pc + 4 + B7_0s * 2); +	ctx->bstate = BS_BRANCH; +	return; +    case 0x8f00:		/* bf/s label */ +	CHECK_NOT_DELAY_SLOT +	gen_branch_slot(ctx->delayed_pc = ctx->pc + 4 + B7_0s * 2, 0); +	ctx->flags |= DELAY_SLOT_CONDITIONAL; +	return; +    case 0x8900:		/* bt label */ +	CHECK_NOT_DELAY_SLOT +	    gen_conditional_jump(ctx, ctx->pc + 4 + B7_0s * 2, +				 ctx->pc + 2); +	ctx->bstate = BS_BRANCH; +	return; +    case 0x8d00:		/* bt/s label */ +	CHECK_NOT_DELAY_SLOT +	gen_branch_slot(ctx->delayed_pc = ctx->pc + 4 + B7_0s * 2, 1); +	ctx->flags |= DELAY_SLOT_CONDITIONAL; +	return; +    case 0x8800:		/* cmp/eq #imm,R0 */ +        tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(0), B7_0s); +	return; +    case 0xc400:		/* mov.b @(disp,GBR),R0 */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0); +            tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_SB); +	    tcg_temp_free(addr); +	} +	return; +    case 0xc500:		/* mov.w @(disp,GBR),R0 */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2); +            tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESW); +	    tcg_temp_free(addr); +	} +	return; +    case 0xc600:		/* mov.l @(disp,GBR),R0 */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4); +            tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESL); +	    tcg_temp_free(addr); +	} +	return; +    case 0xc000:		/* mov.b R0,@(disp,GBR) */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0); +            tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_UB); +	    tcg_temp_free(addr); +	} +	return; +    case 0xc100:		/* mov.w R0,@(disp,GBR) */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 2); +            tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUW); +	    tcg_temp_free(addr); +	} +	return; +    case 0xc200:		/* mov.l R0,@(disp,GBR) */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, cpu_gbr, B7_0 * 4); +            tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUL); +	    tcg_temp_free(addr); +	} +	return; +    case 0x8000:		/* mov.b R0,@(disp,Rn) */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, REG(B7_4), B3_0); +            tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_UB); +	    tcg_temp_free(addr); +	} +	return; +    case 0x8100:		/* mov.w R0,@(disp,Rn) */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2); +            tcg_gen_qemu_st_i32(REG(0), addr, ctx->memidx, MO_TEUW); +	    tcg_temp_free(addr); +	} +	return; +    case 0x8400:		/* mov.b @(disp,Rn),R0 */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, REG(B7_4), B3_0); +            tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_SB); +	    tcg_temp_free(addr); +	} +	return; +    case 0x8500:		/* mov.w @(disp,Rn),R0 */ +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_addi_i32(addr, REG(B7_4), B3_0 * 2); +            tcg_gen_qemu_ld_i32(REG(0), addr, ctx->memidx, MO_TESW); +	    tcg_temp_free(addr); +	} +	return; +    case 0xc700:		/* mova @(disp,PC),R0 */ +	tcg_gen_movi_i32(REG(0), ((ctx->pc & 0xfffffffc) + 4 + B7_0 * 4) & ~3); +	return; +    case 0xcb00:		/* or #imm,R0 */ +	tcg_gen_ori_i32(REG(0), REG(0), B7_0); +	return; +    case 0xcf00:		/* or.b #imm,@(R0,GBR) */ +	{ +	    TCGv addr, val; +	    addr = tcg_temp_new(); +	    tcg_gen_add_i32(addr, REG(0), cpu_gbr); +	    val = tcg_temp_new(); +            tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB); +	    tcg_gen_ori_i32(val, val, B7_0); +            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB); +	    tcg_temp_free(val); +	    tcg_temp_free(addr); +	} +	return; +    case 0xc300:		/* trapa #imm */ +	{ +	    TCGv imm; +	    CHECK_NOT_DELAY_SLOT +            tcg_gen_movi_i32(cpu_pc, ctx->pc); +	    imm = tcg_const_i32(B7_0); +            gen_helper_trapa(cpu_env, imm); +	    tcg_temp_free(imm); +	    ctx->bstate = BS_BRANCH; +	} +	return; +    case 0xc800:		/* tst #imm,R0 */ +	{ +	    TCGv val = tcg_temp_new(); +	    tcg_gen_andi_i32(val, REG(0), B7_0); +            tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); +	    tcg_temp_free(val); +	} +	return; +    case 0xcc00:		/* tst.b #imm,@(R0,GBR) */ +	{ +	    TCGv val = tcg_temp_new(); +	    tcg_gen_add_i32(val, REG(0), cpu_gbr); +            tcg_gen_qemu_ld_i32(val, val, ctx->memidx, MO_UB); +	    tcg_gen_andi_i32(val, val, B7_0); +            tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); +	    tcg_temp_free(val); +	} +	return; +    case 0xca00:		/* xor #imm,R0 */ +	tcg_gen_xori_i32(REG(0), REG(0), B7_0); +	return; +    case 0xce00:		/* xor.b #imm,@(R0,GBR) */ +	{ +	    TCGv addr, val; +	    addr = tcg_temp_new(); +	    tcg_gen_add_i32(addr, REG(0), cpu_gbr); +	    val = tcg_temp_new(); +            tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB); +	    tcg_gen_xori_i32(val, val, B7_0); +            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB); +	    tcg_temp_free(val); +	    tcg_temp_free(addr); +	} +	return; +    } + +    switch (ctx->opcode & 0xf08f) { +    case 0x408e:		/* ldc Rm,Rn_BANK */ +	CHECK_PRIVILEGED +	tcg_gen_mov_i32(ALTREG(B6_4), REG(B11_8)); +	return; +    case 0x4087:		/* ldc.l @Rm+,Rn_BANK */ +	CHECK_PRIVILEGED +        tcg_gen_qemu_ld_i32(ALTREG(B6_4), REG(B11_8), ctx->memidx, MO_TESL); +	tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); +	return; +    case 0x0082:		/* stc Rm_BANK,Rn */ +	CHECK_PRIVILEGED +	tcg_gen_mov_i32(REG(B11_8), ALTREG(B6_4)); +	return; +    case 0x4083:		/* stc.l Rm_BANK,@-Rn */ +	CHECK_PRIVILEGED +	{ +	    TCGv addr = tcg_temp_new(); +	    tcg_gen_subi_i32(addr, REG(B11_8), 4); +            tcg_gen_qemu_st_i32(ALTREG(B6_4), addr, ctx->memidx, MO_TEUL); +	    tcg_gen_mov_i32(REG(B11_8), addr); +	    tcg_temp_free(addr); +	} +	return; +    } + +    switch (ctx->opcode & 0xf0ff) { +    case 0x0023:		/* braf Rn */ +	CHECK_NOT_DELAY_SLOT +	tcg_gen_addi_i32(cpu_delayed_pc, REG(B11_8), ctx->pc + 4); +	ctx->flags |= DELAY_SLOT; +	ctx->delayed_pc = (uint32_t) - 1; +	return; +    case 0x0003:		/* bsrf Rn */ +	CHECK_NOT_DELAY_SLOT +	tcg_gen_movi_i32(cpu_pr, ctx->pc + 4); +	tcg_gen_add_i32(cpu_delayed_pc, REG(B11_8), cpu_pr); +	ctx->flags |= DELAY_SLOT; +	ctx->delayed_pc = (uint32_t) - 1; +	return; +    case 0x4015:		/* cmp/pl Rn */ +        tcg_gen_setcondi_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), 0); +	return; +    case 0x4011:		/* cmp/pz Rn */ +        tcg_gen_setcondi_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), 0); +	return; +    case 0x4010:		/* dt Rn */ +	tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 1); +        tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), 0); +	return; +    case 0x402b:		/* jmp @Rn */ +	CHECK_NOT_DELAY_SLOT +	tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8)); +	ctx->flags |= DELAY_SLOT; +	ctx->delayed_pc = (uint32_t) - 1; +	return; +    case 0x400b:		/* jsr @Rn */ +	CHECK_NOT_DELAY_SLOT +	tcg_gen_movi_i32(cpu_pr, ctx->pc + 4); +	tcg_gen_mov_i32(cpu_delayed_pc, REG(B11_8)); +	ctx->flags |= DELAY_SLOT; +	ctx->delayed_pc = (uint32_t) - 1; +	return; +    case 0x400e:		/* ldc Rm,SR */ +	CHECK_PRIVILEGED +        { +            TCGv val = tcg_temp_new(); +            tcg_gen_andi_i32(val, REG(B11_8), 0x700083f3); +            gen_write_sr(val); +            tcg_temp_free(val); +            ctx->bstate = BS_STOP; +        } +	return; +    case 0x4007:		/* ldc.l @Rm+,SR */ +	CHECK_PRIVILEGED +	{ +	    TCGv val = tcg_temp_new(); +            tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TESL); +            tcg_gen_andi_i32(val, val, 0x700083f3); +            gen_write_sr(val); +	    tcg_temp_free(val); +	    tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); +	    ctx->bstate = BS_STOP; +	} +	return; +    case 0x0002:		/* stc SR,Rn */ +	CHECK_PRIVILEGED +        gen_read_sr(REG(B11_8)); +	return; +    case 0x4003:		/* stc SR,@-Rn */ +	CHECK_PRIVILEGED +	{ +	    TCGv addr = tcg_temp_new(); +            TCGv val = tcg_temp_new(); +	    tcg_gen_subi_i32(addr, REG(B11_8), 4); +            gen_read_sr(val); +            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL); +	    tcg_gen_mov_i32(REG(B11_8), addr); +            tcg_temp_free(val); +	    tcg_temp_free(addr); +	} +	return; +#define LD(reg,ldnum,ldpnum,prechk)		\ +  case ldnum:							\ +    prechk    							\ +    tcg_gen_mov_i32 (cpu_##reg, REG(B11_8));			\ +    return;							\ +  case ldpnum:							\ +    prechk    							\ +    tcg_gen_qemu_ld_i32(cpu_##reg, REG(B11_8), ctx->memidx, MO_TESL); \ +    tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4);		\ +    return; +#define ST(reg,stnum,stpnum,prechk)		\ +  case stnum:							\ +    prechk    							\ +    tcg_gen_mov_i32 (REG(B11_8), cpu_##reg);			\ +    return;							\ +  case stpnum:							\ +    prechk    							\ +    {								\ +	TCGv addr = tcg_temp_new();				\ +	tcg_gen_subi_i32(addr, REG(B11_8), 4);			\ +        tcg_gen_qemu_st_i32(cpu_##reg, addr, ctx->memidx, MO_TEUL); \ +	tcg_gen_mov_i32(REG(B11_8), addr);			\ +	tcg_temp_free(addr);					\ +    }								\ +    return; +#define LDST(reg,ldnum,ldpnum,stnum,stpnum,prechk)		\ +	LD(reg,ldnum,ldpnum,prechk)				\ +	ST(reg,stnum,stpnum,prechk) +	LDST(gbr,  0x401e, 0x4017, 0x0012, 0x4013, {}) +	LDST(vbr,  0x402e, 0x4027, 0x0022, 0x4023, CHECK_PRIVILEGED) +	LDST(ssr,  0x403e, 0x4037, 0x0032, 0x4033, CHECK_PRIVILEGED) +	LDST(spc,  0x404e, 0x4047, 0x0042, 0x4043, CHECK_PRIVILEGED) +	ST(sgr,  0x003a, 0x4032, CHECK_PRIVILEGED) +	LD(sgr,  0x403a, 0x4036, CHECK_PRIVILEGED if (!(ctx->features & SH_FEATURE_SH4A)) break;) +	LDST(dbr,  0x40fa, 0x40f6, 0x00fa, 0x40f2, CHECK_PRIVILEGED) +	LDST(mach, 0x400a, 0x4006, 0x000a, 0x4002, {}) +	LDST(macl, 0x401a, 0x4016, 0x001a, 0x4012, {}) +	LDST(pr,   0x402a, 0x4026, 0x002a, 0x4022, {}) +	LDST(fpul, 0x405a, 0x4056, 0x005a, 0x4052, {CHECK_FPU_ENABLED}) +    case 0x406a:		/* lds Rm,FPSCR */ +	CHECK_FPU_ENABLED +        gen_helper_ld_fpscr(cpu_env, REG(B11_8)); +	ctx->bstate = BS_STOP; +	return; +    case 0x4066:		/* lds.l @Rm+,FPSCR */ +	CHECK_FPU_ENABLED +	{ +	    TCGv addr = tcg_temp_new(); +            tcg_gen_qemu_ld_i32(addr, REG(B11_8), ctx->memidx, MO_TESL); +	    tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); +            gen_helper_ld_fpscr(cpu_env, addr); +	    tcg_temp_free(addr); +	    ctx->bstate = BS_STOP; +	} +	return; +    case 0x006a:		/* sts FPSCR,Rn */ +	CHECK_FPU_ENABLED +	tcg_gen_andi_i32(REG(B11_8), cpu_fpscr, 0x003fffff); +	return; +    case 0x4062:		/* sts FPSCR,@-Rn */ +	CHECK_FPU_ENABLED +	{ +	    TCGv addr, val; +	    val = tcg_temp_new(); +	    tcg_gen_andi_i32(val, cpu_fpscr, 0x003fffff); +	    addr = tcg_temp_new(); +	    tcg_gen_subi_i32(addr, REG(B11_8), 4); +            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL); +	    tcg_gen_mov_i32(REG(B11_8), addr); +	    tcg_temp_free(addr); +	    tcg_temp_free(val); +	} +	return; +    case 0x00c3:		/* movca.l R0,@Rm */ +        { +            TCGv val = tcg_temp_new(); +            tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TEUL); +            gen_helper_movcal(cpu_env, REG(B11_8), val); +            tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL); +        } +        ctx->has_movcal = 1; +	return; +    case 0x40a9: +	/* MOVUA.L @Rm,R0 (Rm) -> R0 +	   Load non-boundary-aligned data */ +        tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL); +	return; +    case 0x40e9: +	/* MOVUA.L @Rm+,R0   (Rm) -> R0, Rm + 4 -> Rm +	   Load non-boundary-aligned data */ +        tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL); +	tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); +	return; +    case 0x0029:		/* movt Rn */ +        tcg_gen_mov_i32(REG(B11_8), cpu_sr_t); +	return; +    case 0x0073: +        /* MOVCO.L +	       LDST -> T +               If (T == 1) R0 -> (Rn) +               0 -> LDST +        */ +        if (ctx->features & SH_FEATURE_SH4A) { +            TCGLabel *label = gen_new_label(); +            tcg_gen_mov_i32(cpu_sr_t, cpu_ldst); +	    tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ldst, 0, label); +            tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL); +	    gen_set_label(label); +	    tcg_gen_movi_i32(cpu_ldst, 0); +	    return; +	} else +	    break; +    case 0x0063: +        /* MOVLI.L @Rm,R0 +               1 -> LDST +               (Rm) -> R0 +               When interrupt/exception +               occurred 0 -> LDST +        */ +	if (ctx->features & SH_FEATURE_SH4A) { +	    tcg_gen_movi_i32(cpu_ldst, 0); +            tcg_gen_qemu_ld_i32(REG(0), REG(B11_8), ctx->memidx, MO_TESL); +	    tcg_gen_movi_i32(cpu_ldst, 1); +	    return; +	} else +	    break; +    case 0x0093:		/* ocbi @Rn */ +	{ +            gen_helper_ocbi(cpu_env, REG(B11_8)); +	} +	return; +    case 0x00a3:		/* ocbp @Rn */ +    case 0x00b3:		/* ocbwb @Rn */ +        /* These instructions are supposed to do nothing in case of +           a cache miss. Given that we only partially emulate caches +           it is safe to simply ignore them. */ +	return; +    case 0x0083:		/* pref @Rn */ +	return; +    case 0x00d3:		/* prefi @Rn */ +	if (ctx->features & SH_FEATURE_SH4A) +	    return; +	else +	    break; +    case 0x00e3:		/* icbi @Rn */ +	if (ctx->features & SH_FEATURE_SH4A) +	    return; +	else +	    break; +    case 0x00ab:		/* synco */ +	if (ctx->features & SH_FEATURE_SH4A) +	    return; +	else +	    break; +    case 0x4024:		/* rotcl Rn */ +	{ +	    TCGv tmp = tcg_temp_new(); +            tcg_gen_mov_i32(tmp, cpu_sr_t); +            tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31); +	    tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); +            tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp); +	    tcg_temp_free(tmp); +	} +	return; +    case 0x4025:		/* rotcr Rn */ +	{ +	    TCGv tmp = tcg_temp_new(); +            tcg_gen_shli_i32(tmp, cpu_sr_t, 31); +            tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); +	    tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); +            tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp); +	    tcg_temp_free(tmp); +	} +	return; +    case 0x4004:		/* rotl Rn */ +	tcg_gen_rotli_i32(REG(B11_8), REG(B11_8), 1); +        tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0); +	return; +    case 0x4005:		/* rotr Rn */ +        tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0); +	tcg_gen_rotri_i32(REG(B11_8), REG(B11_8), 1); +	return; +    case 0x4000:		/* shll Rn */ +    case 0x4020:		/* shal Rn */ +        tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31); +	tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); +	return; +    case 0x4021:		/* shar Rn */ +        tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); +	tcg_gen_sari_i32(REG(B11_8), REG(B11_8), 1); +	return; +    case 0x4001:		/* shlr Rn */ +        tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); +	tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); +	return; +    case 0x4008:		/* shll2 Rn */ +	tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 2); +	return; +    case 0x4018:		/* shll8 Rn */ +	tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 8); +	return; +    case 0x4028:		/* shll16 Rn */ +	tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 16); +	return; +    case 0x4009:		/* shlr2 Rn */ +	tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 2); +	return; +    case 0x4019:		/* shlr8 Rn */ +	tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 8); +	return; +    case 0x4029:		/* shlr16 Rn */ +	tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 16); +	return; +    case 0x401b:		/* tas.b @Rn */ +	{ +	    TCGv addr, val; +	    addr = tcg_temp_local_new(); +	    tcg_gen_mov_i32(addr, REG(B11_8)); +	    val = tcg_temp_local_new(); +            tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB); +            tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); +	    tcg_gen_ori_i32(val, val, 0x80); +            tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB); +	    tcg_temp_free(val); +	    tcg_temp_free(addr); +	} +	return; +    case 0xf00d: /* fsts FPUL,FRn - FPSCR: Nothing */ +	CHECK_FPU_ENABLED +	tcg_gen_mov_i32(cpu_fregs[FREG(B11_8)], cpu_fpul); +	return; +    case 0xf01d: /* flds FRm,FPUL - FPSCR: Nothing */ +	CHECK_FPU_ENABLED +	tcg_gen_mov_i32(cpu_fpul, cpu_fregs[FREG(B11_8)]); +	return; +    case 0xf02d: /* float FPUL,FRn/DRn - FPSCR: R[PR,Enable.I]/W[Cause,Flag] */ +	CHECK_FPU_ENABLED +        if (ctx->flags & FPSCR_PR) { +	    TCGv_i64 fp; +	    if (ctx->opcode & 0x0100) +		break; /* illegal instruction */ +	    fp = tcg_temp_new_i64(); +            gen_helper_float_DT(fp, cpu_env, cpu_fpul); +	    gen_store_fpr64(fp, DREG(B11_8)); +	    tcg_temp_free_i64(fp); +	} +	else { +            gen_helper_float_FT(cpu_fregs[FREG(B11_8)], cpu_env, cpu_fpul); +	} +	return; +    case 0xf03d: /* ftrc FRm/DRm,FPUL - FPSCR: R[PR,Enable.V]/W[Cause,Flag] */ +	CHECK_FPU_ENABLED +        if (ctx->flags & FPSCR_PR) { +	    TCGv_i64 fp; +	    if (ctx->opcode & 0x0100) +		break; /* illegal instruction */ +	    fp = tcg_temp_new_i64(); +	    gen_load_fpr64(fp, DREG(B11_8)); +            gen_helper_ftrc_DT(cpu_fpul, cpu_env, fp); +	    tcg_temp_free_i64(fp); +	} +	else { +            gen_helper_ftrc_FT(cpu_fpul, cpu_env, cpu_fregs[FREG(B11_8)]); +	} +	return; +    case 0xf04d: /* fneg FRn/DRn - FPSCR: Nothing */ +	CHECK_FPU_ENABLED +	{ +	    gen_helper_fneg_T(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)]); +	} +	return; +    case 0xf05d: /* fabs FRn/DRn */ +	CHECK_FPU_ENABLED +        if (ctx->flags & FPSCR_PR) { +	    if (ctx->opcode & 0x0100) +		break; /* illegal instruction */ +	    TCGv_i64 fp = tcg_temp_new_i64(); +	    gen_load_fpr64(fp, DREG(B11_8)); +	    gen_helper_fabs_DT(fp, fp); +	    gen_store_fpr64(fp, DREG(B11_8)); +	    tcg_temp_free_i64(fp); +	} else { +	    gen_helper_fabs_FT(cpu_fregs[FREG(B11_8)], cpu_fregs[FREG(B11_8)]); +	} +	return; +    case 0xf06d: /* fsqrt FRn */ +	CHECK_FPU_ENABLED +        if (ctx->flags & FPSCR_PR) { +	    if (ctx->opcode & 0x0100) +		break; /* illegal instruction */ +	    TCGv_i64 fp = tcg_temp_new_i64(); +	    gen_load_fpr64(fp, DREG(B11_8)); +            gen_helper_fsqrt_DT(fp, cpu_env, fp); +	    gen_store_fpr64(fp, DREG(B11_8)); +	    tcg_temp_free_i64(fp); +	} else { +            gen_helper_fsqrt_FT(cpu_fregs[FREG(B11_8)], cpu_env, +                                cpu_fregs[FREG(B11_8)]); +	} +	return; +    case 0xf07d: /* fsrra FRn */ +	CHECK_FPU_ENABLED +	break; +    case 0xf08d: /* fldi0 FRn - FPSCR: R[PR] */ +	CHECK_FPU_ENABLED +        if (!(ctx->flags & FPSCR_PR)) { +	    tcg_gen_movi_i32(cpu_fregs[FREG(B11_8)], 0); +	} +	return; +    case 0xf09d: /* fldi1 FRn - FPSCR: R[PR] */ +	CHECK_FPU_ENABLED +        if (!(ctx->flags & FPSCR_PR)) { +	    tcg_gen_movi_i32(cpu_fregs[FREG(B11_8)], 0x3f800000); +	} +	return; +    case 0xf0ad: /* fcnvsd FPUL,DRn */ +	CHECK_FPU_ENABLED +	{ +	    TCGv_i64 fp = tcg_temp_new_i64(); +            gen_helper_fcnvsd_FT_DT(fp, cpu_env, cpu_fpul); +	    gen_store_fpr64(fp, DREG(B11_8)); +	    tcg_temp_free_i64(fp); +	} +	return; +    case 0xf0bd: /* fcnvds DRn,FPUL */ +	CHECK_FPU_ENABLED +	{ +	    TCGv_i64 fp = tcg_temp_new_i64(); +	    gen_load_fpr64(fp, DREG(B11_8)); +            gen_helper_fcnvds_DT_FT(cpu_fpul, cpu_env, fp); +	    tcg_temp_free_i64(fp); +	} +	return; +    case 0xf0ed: /* fipr FVm,FVn */ +        CHECK_FPU_ENABLED +        if ((ctx->flags & FPSCR_PR) == 0) { +            TCGv m, n; +            m = tcg_const_i32((ctx->opcode >> 8) & 3); +            n = tcg_const_i32((ctx->opcode >> 10) & 3); +            gen_helper_fipr(cpu_env, m, n); +            tcg_temp_free(m); +            tcg_temp_free(n); +            return; +        } +        break; +    case 0xf0fd: /* ftrv XMTRX,FVn */ +        CHECK_FPU_ENABLED +        if ((ctx->opcode & 0x0300) == 0x0100 && +            (ctx->flags & FPSCR_PR) == 0) { +            TCGv n; +            n = tcg_const_i32((ctx->opcode >> 10) & 3); +            gen_helper_ftrv(cpu_env, n); +            tcg_temp_free(n); +            return; +        } +        break; +    } +#if 0 +    fprintf(stderr, "unknown instruction 0x%04x at pc 0x%08x\n", +	    ctx->opcode, ctx->pc); +    fflush(stderr); +#endif +    tcg_gen_movi_i32(cpu_pc, ctx->pc); +    if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { +        gen_helper_raise_slot_illegal_instruction(cpu_env); +    } else { +        gen_helper_raise_illegal_instruction(cpu_env); +    } +    ctx->bstate = BS_BRANCH; +} + +static void decode_opc(DisasContext * ctx) +{ +    uint32_t old_flags = ctx->flags; + +    if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { +        tcg_gen_debug_insn_start(ctx->pc); +    } + +    _decode_opc(ctx); + +    if (old_flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { +        if (ctx->flags & DELAY_SLOT_CLEARME) { +            gen_store_flags(0); +        } else { +	    /* go out of the delay slot */ +	    uint32_t new_flags = ctx->flags; +	    new_flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL); +	    gen_store_flags(new_flags); +        } +        ctx->flags = 0; +        ctx->bstate = BS_BRANCH; +        if (old_flags & DELAY_SLOT_CONDITIONAL) { +	    gen_delayed_conditional_jump(ctx); +        } else if (old_flags & DELAY_SLOT) { +            gen_jump(ctx); +	} + +    } + +    /* go into a delay slot */ +    if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) +        gen_store_flags(ctx->flags); +} + +static inline void +gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, +                               bool search_pc) +{ +    CPUState *cs = CPU(cpu); +    CPUSH4State *env = &cpu->env; +    DisasContext ctx; +    target_ulong pc_start; +    CPUBreakpoint *bp; +    int i, ii; +    int num_insns; +    int max_insns; + +    pc_start = tb->pc; +    ctx.pc = pc_start; +    ctx.flags = (uint32_t)tb->flags; +    ctx.bstate = BS_NONE; +    ctx.memidx = (ctx.flags & (1u << SR_MD)) == 0 ? 1 : 0; +    /* We don't know if the delayed pc came from a dynamic or static branch, +       so assume it is a dynamic branch.  */ +    ctx.delayed_pc = -1; /* use delayed pc from env pointer */ +    ctx.tb = tb; +    ctx.singlestep_enabled = cs->singlestep_enabled; +    ctx.features = env->features; +    ctx.has_movcal = (ctx.flags & TB_FLAG_PENDING_MOVCA); + +    ii = -1; +    num_insns = 0; +    max_insns = tb->cflags & CF_COUNT_MASK; +    if (max_insns == 0) +        max_insns = CF_COUNT_MASK; +    gen_tb_start(tb); +    while (ctx.bstate == BS_NONE && !tcg_op_buf_full()) { +        if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { +            QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { +                if (ctx.pc == bp->pc) { +		    /* We have hit a breakpoint - make sure PC is up-to-date */ +		    tcg_gen_movi_i32(cpu_pc, ctx.pc); +                    gen_helper_debug(cpu_env); +                    ctx.bstate = BS_BRANCH; +		    break; +		} +	    } +	} +        if (search_pc) { +            i = tcg_op_buf_count(); +            if (ii < i) { +                ii++; +                while (ii < i) +                    tcg_ctx.gen_opc_instr_start[ii++] = 0; +            } +            tcg_ctx.gen_opc_pc[ii] = ctx.pc; +            gen_opc_hflags[ii] = ctx.flags; +            tcg_ctx.gen_opc_instr_start[ii] = 1; +            tcg_ctx.gen_opc_icount[ii] = num_insns; +        } +        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) +            gen_io_start(); +#if 0 +	fprintf(stderr, "Loading opcode at address 0x%08x\n", ctx.pc); +	fflush(stderr); +#endif +        ctx.opcode = cpu_lduw_code(env, ctx.pc); +	decode_opc(&ctx); +        num_insns++; +	ctx.pc += 2; +	if ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0) +	    break; +        if (cs->singlestep_enabled) { +	    break; +        } +        if (num_insns >= max_insns) +            break; +        if (singlestep) +            break; +    } +    if (tb->cflags & CF_LAST_IO) +        gen_io_end(); +    if (cs->singlestep_enabled) { +        tcg_gen_movi_i32(cpu_pc, ctx.pc); +        gen_helper_debug(cpu_env); +    } else { +	switch (ctx.bstate) { +        case BS_STOP: +            /* gen_op_interrupt_restart(); */ +            /* fall through */ +        case BS_NONE: +            if (ctx.flags) { +                gen_store_flags(ctx.flags | DELAY_SLOT_CLEARME); +	    } +            gen_goto_tb(&ctx, 0, ctx.pc); +            break; +        case BS_EXCP: +            /* gen_op_interrupt_restart(); */ +            tcg_gen_exit_tb(0); +            break; +        case BS_BRANCH: +        default: +            break; +	} +    } + +    gen_tb_end(tb, num_insns); + +    if (search_pc) { +        i = tcg_op_buf_count(); +        ii++; +        while (ii <= i) +            tcg_ctx.gen_opc_instr_start[ii++] = 0; +    } else { +        tb->size = ctx.pc - pc_start; +        tb->icount = num_insns; +    } + +#ifdef DEBUG_DISAS +    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { +	qemu_log("IN:\n");	/* , lookup_symbol(pc_start)); */ +        log_target_disas(cs, pc_start, ctx.pc - pc_start, 0); +	qemu_log("\n"); +    } +#endif +} + +void gen_intermediate_code(CPUSH4State * env, struct TranslationBlock *tb) +{ +    gen_intermediate_code_internal(sh_env_get_cpu(env), tb, false); +} + +void gen_intermediate_code_pc(CPUSH4State * env, struct TranslationBlock *tb) +{ +    gen_intermediate_code_internal(sh_env_get_cpu(env), tb, true); +} + +void restore_state_to_opc(CPUSH4State *env, TranslationBlock *tb, int pc_pos) +{ +    env->pc = tcg_ctx.gen_opc_pc[pc_pos]; +    env->flags = gen_opc_hflags[pc_pos]; +}  | 
