diff options
Diffstat (limited to 'target-mips')
| -rw-r--r-- | target-mips/Makefile.objs | 4 | ||||
| -rw-r--r-- | target-mips/TODO | 51 | ||||
| -rw-r--r-- | target-mips/cpu-qom.h | 91 | ||||
| -rw-r--r-- | target-mips/cpu.c | 180 | ||||
| -rw-r--r-- | target-mips/cpu.h | 1052 | ||||
| -rw-r--r-- | target-mips/dsp_helper.c | 3761 | ||||
| -rw-r--r-- | target-mips/gdbstub.c | 150 | ||||
| -rw-r--r-- | target-mips/helper.c | 840 | ||||
| -rw-r--r-- | target-mips/helper.h | 944 | ||||
| -rw-r--r-- | target-mips/kvm.c | 689 | ||||
| -rw-r--r-- | target-mips/kvm_mips.h | 26 | ||||
| -rw-r--r-- | target-mips/lmi_helper.c | 744 | ||||
| -rw-r--r-- | target-mips/machine.c | 298 | ||||
| -rw-r--r-- | target-mips/mips-defs.h | 91 | ||||
| -rw-r--r-- | target-mips/mips-semi.c | 373 | ||||
| -rw-r--r-- | target-mips/msa_helper.c | 3433 | ||||
| -rw-r--r-- | target-mips/op_helper.c | 3706 | ||||
| -rw-r--r-- | target-mips/translate.c | 20731 | ||||
| -rw-r--r-- | target-mips/translate_init.c | 883 | 
19 files changed, 38047 insertions, 0 deletions
diff --git a/target-mips/Makefile.objs b/target-mips/Makefile.objs new file mode 100644 index 00000000..bc5ed851 --- /dev/null +++ b/target-mips/Makefile.objs @@ -0,0 +1,4 @@ +obj-y += translate.o dsp_helper.o op_helper.o lmi_helper.o helper.o cpu.o +obj-y += gdbstub.o msa_helper.o mips-semi.o +obj-$(CONFIG_SOFTMMU) += machine.o +obj-$(CONFIG_KVM) += kvm.o diff --git a/target-mips/TODO b/target-mips/TODO new file mode 100644 index 00000000..1d782d80 --- /dev/null +++ b/target-mips/TODO @@ -0,0 +1,51 @@ +Unsolved issues/bugs in the mips/mipsel backend +----------------------------------------------- + +General +------- +- Unimplemented ASEs: +  - MDMX +  - SmartMIPS +  - microMIPS DSP r1 & r2 encodings +- MT ASE only partially implemented and not functional +- Shadow register support only partially implemented, +  lacks set switching on interrupt/exception. +- 34K ITC not implemented. +- A general lack of documentation, especially for technical internals. +  Existing documentation is x86-centric. +- Reverse endianness bit not implemented +- The TLB emulation is very inefficient: +  QEMU's softmmu implements a x86-style MMU, with separate entries +  for read/write/execute, a TLB index which is just a modulo of the +  virtual address, and a set of TLBs for each user/kernel/supervisor +  MMU mode. +  MIPS has a single entry for read/write/execute and only one MMU mode. +  But it is fully associative with randomized entry indices, and uses +  up to 256 ASID tags as additional matching criterion (which roughly +  equates to 256 MMU modes). It also has a global flag which causes +  entries to match regardless of ASID. +  To cope with these differences, QEMU currently flushes the TLB at +  each ASID change. Using the MMU modes to implement ASIDs hinges on +  implementing the global bit efficiently. +- save/restore of the CPU state is not implemented (see machine.c). + +MIPS64 +------ +- Userland emulation (both n32 and n64) not functional. + +"Generic" 4Kc system emulation +------------------------------ +- Doesn't correspond to any real hardware. Should be removed some day, +  U-Boot is the last remaining user. + +PICA 61 system emulation +------------------------ +- No framebuffer support yet. + +MALTA system emulation +---------------------- +- We fake firmware support instead of doing the real thing +- Real firmware (YAMON) falls over when trying to init RAM, presumably +  due to lacking system controller emulation. +- Bonito system controller not implemented +- MSC1 system controller not implemented diff --git a/target-mips/cpu-qom.h b/target-mips/cpu-qom.h new file mode 100644 index 00000000..4d6f9de2 --- /dev/null +++ b/target-mips/cpu-qom.h @@ -0,0 +1,91 @@ +/* + * QEMU MIPS 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_MIPS_CPU_QOM_H +#define QEMU_MIPS_CPU_QOM_H + +#include "qom/cpu.h" + +#ifdef TARGET_MIPS64 +#define TYPE_MIPS_CPU "mips64-cpu" +#else +#define TYPE_MIPS_CPU "mips-cpu" +#endif + +#define MIPS_CPU_CLASS(klass) \ +    OBJECT_CLASS_CHECK(MIPSCPUClass, (klass), TYPE_MIPS_CPU) +#define MIPS_CPU(obj) \ +    OBJECT_CHECK(MIPSCPU, (obj), TYPE_MIPS_CPU) +#define MIPS_CPU_GET_CLASS(obj) \ +    OBJECT_GET_CLASS(MIPSCPUClass, (obj), TYPE_MIPS_CPU) + +/** + * MIPSCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A MIPS CPU model. + */ +typedef struct MIPSCPUClass { +    /*< private >*/ +    CPUClass parent_class; +    /*< public >*/ + +    DeviceRealize parent_realize; +    void (*parent_reset)(CPUState *cpu); +} MIPSCPUClass; + +/** + * MIPSCPU: + * @env: #CPUMIPSState + * + * A MIPS CPU. + */ +typedef struct MIPSCPU { +    /*< private >*/ +    CPUState parent_obj; +    /*< public >*/ + +    CPUMIPSState env; +} MIPSCPU; + +static inline MIPSCPU *mips_env_get_cpu(CPUMIPSState *env) +{ +    return container_of(env, MIPSCPU, env); +} + +#define ENV_GET_CPU(e) CPU(mips_env_get_cpu(e)) + +#define ENV_OFFSET offsetof(MIPSCPU, env) + +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vmstate_mips_cpu; +#endif + +void mips_cpu_do_interrupt(CPUState *cpu); +bool mips_cpu_exec_interrupt(CPUState *cpu, int int_req); +void mips_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, +                         int flags); +hwaddr mips_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int mips_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int mips_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +void mips_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, +                                  int is_write, int is_user, uintptr_t retaddr); + +#endif diff --git a/target-mips/cpu.c b/target-mips/cpu.c new file mode 100644 index 00000000..7fe1f040 --- /dev/null +++ b/target-mips/cpu.c @@ -0,0 +1,180 @@ +/* + * QEMU MIPS 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> + */ + +#include "cpu.h" +#include "kvm_mips.h" +#include "qemu-common.h" +#include "sysemu/kvm.h" + + +static void mips_cpu_set_pc(CPUState *cs, vaddr value) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; + +    env->active_tc.PC = value & ~(target_ulong)1; +    if (value & 1) { +        env->hflags |= MIPS_HFLAG_M16; +    } else { +        env->hflags &= ~(MIPS_HFLAG_M16); +    } +} + +static void mips_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; + +    env->active_tc.PC = tb->pc; +    env->hflags &= ~MIPS_HFLAG_BMASK; +    env->hflags |= tb->flags & MIPS_HFLAG_BMASK; +} + +static bool mips_cpu_has_work(CPUState *cs) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    bool has_work = false; + +    /* It is implementation dependent if non-enabled interrupts +       wake-up the CPU, however most of the implementations only +       check for interrupts that can be taken. */ +    if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && +        cpu_mips_hw_interrupts_pending(env)) { +        has_work = true; +    } + +    /* MIPS-MT has the ability to halt the CPU.  */ +    if (env->CP0_Config3 & (1 << CP0C3_MT)) { +        /* The QEMU model will issue an _WAKE request whenever the CPUs +           should be woken up.  */ +        if (cs->interrupt_request & CPU_INTERRUPT_WAKE) { +            has_work = true; +        } + +        if (!mips_vpe_active(env)) { +            has_work = false; +        } +    } +    return has_work; +} + +/* CPUClass::reset() */ +static void mips_cpu_reset(CPUState *s) +{ +    MIPSCPU *cpu = MIPS_CPU(s); +    MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu); +    CPUMIPSState *env = &cpu->env; + +    mcc->parent_reset(s); + +    memset(env, 0, offsetof(CPUMIPSState, mvp)); +    tlb_flush(s, 1); + +    cpu_state_reset(env); + +#ifndef CONFIG_USER_ONLY +    if (kvm_enabled()) { +        kvm_mips_reset_vcpu(cpu); +    } +#endif +} + +static void mips_cpu_realizefn(DeviceState *dev, Error **errp) +{ +    CPUState *cs = CPU(dev); +    MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(dev); + +    cpu_reset(cs); +    qemu_init_vcpu(cs); + +    mcc->parent_realize(dev, errp); +} + +static void mips_cpu_initfn(Object *obj) +{ +    CPUState *cs = CPU(obj); +    MIPSCPU *cpu = MIPS_CPU(obj); +    CPUMIPSState *env = &cpu->env; + +    cs->env_ptr = env; +    cpu_exec_init(cs, &error_abort); + +    if (tcg_enabled()) { +        mips_tcg_init(); +    } +} + +static void mips_cpu_class_init(ObjectClass *c, void *data) +{ +    MIPSCPUClass *mcc = MIPS_CPU_CLASS(c); +    CPUClass *cc = CPU_CLASS(c); +    DeviceClass *dc = DEVICE_CLASS(c); + +    mcc->parent_realize = dc->realize; +    dc->realize = mips_cpu_realizefn; + +    mcc->parent_reset = cc->reset; +    cc->reset = mips_cpu_reset; + +    cc->has_work = mips_cpu_has_work; +    cc->do_interrupt = mips_cpu_do_interrupt; +    cc->cpu_exec_interrupt = mips_cpu_exec_interrupt; +    cc->dump_state = mips_cpu_dump_state; +    cc->set_pc = mips_cpu_set_pc; +    cc->synchronize_from_tb = mips_cpu_synchronize_from_tb; +    cc->gdb_read_register = mips_cpu_gdb_read_register; +    cc->gdb_write_register = mips_cpu_gdb_write_register; +#ifdef CONFIG_USER_ONLY +    cc->handle_mmu_fault = mips_cpu_handle_mmu_fault; +#else +    cc->do_unassigned_access = mips_cpu_unassigned_access; +    cc->do_unaligned_access = mips_cpu_do_unaligned_access; +    cc->get_phys_page_debug = mips_cpu_get_phys_page_debug; +    cc->vmsd = &vmstate_mips_cpu; +#endif + +    cc->gdb_num_core_regs = 73; +    cc->gdb_stop_before_watchpoint = true; + +    /* +     * Reason: mips_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 mips_cpu_type_info = { +    .name = TYPE_MIPS_CPU, +    .parent = TYPE_CPU, +    .instance_size = sizeof(MIPSCPU), +    .instance_init = mips_cpu_initfn, +    .abstract = false, +    .class_size = sizeof(MIPSCPUClass), +    .class_init = mips_cpu_class_init, +}; + +static void mips_cpu_register_types(void) +{ +    type_register_static(&mips_cpu_type_info); +} + +type_init(mips_cpu_register_types) diff --git a/target-mips/cpu.h b/target-mips/cpu.h new file mode 100644 index 00000000..075c561c --- /dev/null +++ b/target-mips/cpu.h @@ -0,0 +1,1052 @@ +#if !defined (__MIPS_CPU_H__) +#define __MIPS_CPU_H__ + +//#define DEBUG_OP + +#define ALIGNED_ONLY + +#define ELF_MACHINE	EM_MIPS + +#define CPUArchState struct CPUMIPSState + +#include "config.h" +#include "qemu-common.h" +#include "mips-defs.h" +#include "exec/cpu-defs.h" +#include "fpu/softfloat.h" + +struct CPUMIPSState; + +typedef struct r4k_tlb_t r4k_tlb_t; +struct r4k_tlb_t { +    target_ulong VPN; +    uint32_t PageMask; +    uint_fast8_t ASID; +    uint_fast16_t G:1; +    uint_fast16_t C0:3; +    uint_fast16_t C1:3; +    uint_fast16_t V0:1; +    uint_fast16_t V1:1; +    uint_fast16_t D0:1; +    uint_fast16_t D1:1; +    uint_fast16_t XI0:1; +    uint_fast16_t XI1:1; +    uint_fast16_t RI0:1; +    uint_fast16_t RI1:1; +    uint_fast16_t EHINV:1; +    uint64_t PFN[2]; +}; + +#if !defined(CONFIG_USER_ONLY) +typedef struct CPUMIPSTLBContext CPUMIPSTLBContext; +struct CPUMIPSTLBContext { +    uint32_t nb_tlb; +    uint32_t tlb_in_use; +    int (*map_address) (struct CPUMIPSState *env, hwaddr *physical, int *prot, target_ulong address, int rw, int access_type); +    void (*helper_tlbwi)(struct CPUMIPSState *env); +    void (*helper_tlbwr)(struct CPUMIPSState *env); +    void (*helper_tlbp)(struct CPUMIPSState *env); +    void (*helper_tlbr)(struct CPUMIPSState *env); +    void (*helper_tlbinv)(struct CPUMIPSState *env); +    void (*helper_tlbinvf)(struct CPUMIPSState *env); +    union { +        struct { +            r4k_tlb_t tlb[MIPS_TLB_MAX]; +        } r4k; +    } mmu; +}; +#endif + +/* MSA Context */ +#define MSA_WRLEN (128) + +enum CPUMIPSMSADataFormat { +    DF_BYTE = 0, +    DF_HALF, +    DF_WORD, +    DF_DOUBLE +}; + +typedef union wr_t wr_t; +union wr_t { +    int8_t  b[MSA_WRLEN/8]; +    int16_t h[MSA_WRLEN/16]; +    int32_t w[MSA_WRLEN/32]; +    int64_t d[MSA_WRLEN/64]; +}; + +typedef union fpr_t fpr_t; +union fpr_t { +    float64  fd;   /* ieee double precision */ +    float32  fs[2];/* ieee single precision */ +    uint64_t d;    /* binary double fixed-point */ +    uint32_t w[2]; /* binary single fixed-point */ +/* FPU/MSA register mapping is not tested on big-endian hosts. */ +    wr_t     wr;   /* vector data */ +}; +/* define FP_ENDIAN_IDX to access the same location + * in the fpr_t union regardless of the host endianness + */ +#if defined(HOST_WORDS_BIGENDIAN) +#  define FP_ENDIAN_IDX 1 +#else +#  define FP_ENDIAN_IDX 0 +#endif + +typedef struct CPUMIPSFPUContext CPUMIPSFPUContext; +struct CPUMIPSFPUContext { +    /* Floating point registers */ +    fpr_t fpr[32]; +    float_status fp_status; +    /* fpu implementation/revision register (fir) */ +    uint32_t fcr0; +#define FCR0_FREP 29 +#define FCR0_UFRP 28 +#define FCR0_F64 22 +#define FCR0_L 21 +#define FCR0_W 20 +#define FCR0_3D 19 +#define FCR0_PS 18 +#define FCR0_D 17 +#define FCR0_S 16 +#define FCR0_PRID 8 +#define FCR0_REV 0 +    /* fcsr */ +    uint32_t fcr31; +#define SET_FP_COND(num,env)     do { ((env).fcr31) |= ((num) ? (1 << ((num) + 24)) : (1 << 23)); } while(0) +#define CLEAR_FP_COND(num,env)   do { ((env).fcr31) &= ~((num) ? (1 << ((num) + 24)) : (1 << 23)); } while(0) +#define GET_FP_COND(env)         ((((env).fcr31 >> 24) & 0xfe) | (((env).fcr31 >> 23) & 0x1)) +#define GET_FP_CAUSE(reg)        (((reg) >> 12) & 0x3f) +#define GET_FP_ENABLE(reg)       (((reg) >>  7) & 0x1f) +#define GET_FP_FLAGS(reg)        (((reg) >>  2) & 0x1f) +#define SET_FP_CAUSE(reg,v)      do { (reg) = ((reg) & ~(0x3f << 12)) | ((v & 0x3f) << 12); } while(0) +#define SET_FP_ENABLE(reg,v)     do { (reg) = ((reg) & ~(0x1f <<  7)) | ((v & 0x1f) << 7); } while(0) +#define SET_FP_FLAGS(reg,v)      do { (reg) = ((reg) & ~(0x1f <<  2)) | ((v & 0x1f) << 2); } while(0) +#define UPDATE_FP_FLAGS(reg,v)   do { (reg) |= ((v & 0x1f) << 2); } while(0) +#define FP_INEXACT        1 +#define FP_UNDERFLOW      2 +#define FP_OVERFLOW       4 +#define FP_DIV0           8 +#define FP_INVALID        16 +#define FP_UNIMPLEMENTED  32 +}; + +#define NB_MMU_MODES 3 + +typedef struct CPUMIPSMVPContext CPUMIPSMVPContext; +struct CPUMIPSMVPContext { +    int32_t CP0_MVPControl; +#define CP0MVPCo_CPA	3 +#define CP0MVPCo_STLB	2 +#define CP0MVPCo_VPC	1 +#define CP0MVPCo_EVP	0 +    int32_t CP0_MVPConf0; +#define CP0MVPC0_M	31 +#define CP0MVPC0_TLBS	29 +#define CP0MVPC0_GS	28 +#define CP0MVPC0_PCP	27 +#define CP0MVPC0_PTLBE	16 +#define CP0MVPC0_TCA	15 +#define CP0MVPC0_PVPE	10 +#define CP0MVPC0_PTC	0 +    int32_t CP0_MVPConf1; +#define CP0MVPC1_CIM	31 +#define CP0MVPC1_CIF	30 +#define CP0MVPC1_PCX	20 +#define CP0MVPC1_PCP2	10 +#define CP0MVPC1_PCP1	0 +}; + +typedef struct mips_def_t mips_def_t; + +#define MIPS_SHADOW_SET_MAX 16 +#define MIPS_TC_MAX 5 +#define MIPS_FPU_MAX 1 +#define MIPS_DSP_ACC 4 +#define MIPS_KSCRATCH_NUM 6 + +typedef struct TCState TCState; +struct TCState { +    target_ulong gpr[32]; +    target_ulong PC; +    target_ulong HI[MIPS_DSP_ACC]; +    target_ulong LO[MIPS_DSP_ACC]; +    target_ulong ACX[MIPS_DSP_ACC]; +    target_ulong DSPControl; +    int32_t CP0_TCStatus; +#define CP0TCSt_TCU3	31 +#define CP0TCSt_TCU2	30 +#define CP0TCSt_TCU1	29 +#define CP0TCSt_TCU0	28 +#define CP0TCSt_TMX	27 +#define CP0TCSt_RNST	23 +#define CP0TCSt_TDS	21 +#define CP0TCSt_DT	20 +#define CP0TCSt_DA	15 +#define CP0TCSt_A	13 +#define CP0TCSt_TKSU	11 +#define CP0TCSt_IXMT	10 +#define CP0TCSt_TASID	0 +    int32_t CP0_TCBind; +#define CP0TCBd_CurTC	21 +#define CP0TCBd_TBE	17 +#define CP0TCBd_CurVPE	0 +    target_ulong CP0_TCHalt; +    target_ulong CP0_TCContext; +    target_ulong CP0_TCSchedule; +    target_ulong CP0_TCScheFBack; +    int32_t CP0_Debug_tcstatus; +    target_ulong CP0_UserLocal; + +    int32_t msacsr; + +#define MSACSR_FS       24 +#define MSACSR_FS_MASK  (1 << MSACSR_FS) +#define MSACSR_NX       18 +#define MSACSR_NX_MASK  (1 << MSACSR_NX) +#define MSACSR_CEF      2 +#define MSACSR_CEF_MASK (0xffff << MSACSR_CEF) +#define MSACSR_RM       0 +#define MSACSR_RM_MASK  (0x3 << MSACSR_RM) +#define MSACSR_MASK     (MSACSR_RM_MASK | MSACSR_CEF_MASK | MSACSR_NX_MASK | \ +        MSACSR_FS_MASK) + +    float_status msa_fp_status; +}; + +typedef struct CPUMIPSState CPUMIPSState; +struct CPUMIPSState { +    TCState active_tc; +    CPUMIPSFPUContext active_fpu; + +    uint32_t current_tc; +    uint32_t current_fpu; + +    uint32_t SEGBITS; +    uint32_t PABITS; +#if defined(TARGET_MIPS64) +# define PABITS_BASE 36 +#else +# define PABITS_BASE 32 +#endif +    target_ulong SEGMask; +    uint64_t PAMask; +#define PAMASK_BASE ((1ULL << PABITS_BASE) - 1) + +    int32_t msair; +#define MSAIR_ProcID    8 +#define MSAIR_Rev       0 + +    int32_t CP0_Index; +    /* CP0_MVP* are per MVP registers. */ +    int32_t CP0_Random; +    int32_t CP0_VPEControl; +#define CP0VPECo_YSI	21 +#define CP0VPECo_GSI	20 +#define CP0VPECo_EXCPT	16 +#define CP0VPECo_TE	15 +#define CP0VPECo_TargTC	0 +    int32_t CP0_VPEConf0; +#define CP0VPEC0_M	31 +#define CP0VPEC0_XTC	21 +#define CP0VPEC0_TCS	19 +#define CP0VPEC0_SCS	18 +#define CP0VPEC0_DSC	17 +#define CP0VPEC0_ICS	16 +#define CP0VPEC0_MVP	1 +#define CP0VPEC0_VPA	0 +    int32_t CP0_VPEConf1; +#define CP0VPEC1_NCX	20 +#define CP0VPEC1_NCP2	10 +#define CP0VPEC1_NCP1	0 +    target_ulong CP0_YQMask; +    target_ulong CP0_VPESchedule; +    target_ulong CP0_VPEScheFBack; +    int32_t CP0_VPEOpt; +#define CP0VPEOpt_IWX7	15 +#define CP0VPEOpt_IWX6	14 +#define CP0VPEOpt_IWX5	13 +#define CP0VPEOpt_IWX4	12 +#define CP0VPEOpt_IWX3	11 +#define CP0VPEOpt_IWX2	10 +#define CP0VPEOpt_IWX1	9 +#define CP0VPEOpt_IWX0	8 +#define CP0VPEOpt_DWX7	7 +#define CP0VPEOpt_DWX6	6 +#define CP0VPEOpt_DWX5	5 +#define CP0VPEOpt_DWX4	4 +#define CP0VPEOpt_DWX3	3 +#define CP0VPEOpt_DWX2	2 +#define CP0VPEOpt_DWX1	1 +#define CP0VPEOpt_DWX0	0 +    uint64_t CP0_EntryLo0; +    uint64_t CP0_EntryLo1; +#if defined(TARGET_MIPS64) +# define CP0EnLo_RI 63 +# define CP0EnLo_XI 62 +#else +# define CP0EnLo_RI 31 +# define CP0EnLo_XI 30 +#endif +    target_ulong CP0_Context; +    target_ulong CP0_KScratch[MIPS_KSCRATCH_NUM]; +    int32_t CP0_PageMask; +    int32_t CP0_PageGrain_rw_bitmask; +    int32_t CP0_PageGrain; +#define CP0PG_RIE 31 +#define CP0PG_XIE 30 +#define CP0PG_ELPA 29 +#define CP0PG_IEC 27 +    int32_t CP0_Wired; +    int32_t CP0_SRSConf0_rw_bitmask; +    int32_t CP0_SRSConf0; +#define CP0SRSC0_M	31 +#define CP0SRSC0_SRS3	20 +#define CP0SRSC0_SRS2	10 +#define CP0SRSC0_SRS1	0 +    int32_t CP0_SRSConf1_rw_bitmask; +    int32_t CP0_SRSConf1; +#define CP0SRSC1_M	31 +#define CP0SRSC1_SRS6	20 +#define CP0SRSC1_SRS5	10 +#define CP0SRSC1_SRS4	0 +    int32_t CP0_SRSConf2_rw_bitmask; +    int32_t CP0_SRSConf2; +#define CP0SRSC2_M	31 +#define CP0SRSC2_SRS9	20 +#define CP0SRSC2_SRS8	10 +#define CP0SRSC2_SRS7	0 +    int32_t CP0_SRSConf3_rw_bitmask; +    int32_t CP0_SRSConf3; +#define CP0SRSC3_M	31 +#define CP0SRSC3_SRS12	20 +#define CP0SRSC3_SRS11	10 +#define CP0SRSC3_SRS10	0 +    int32_t CP0_SRSConf4_rw_bitmask; +    int32_t CP0_SRSConf4; +#define CP0SRSC4_SRS15	20 +#define CP0SRSC4_SRS14	10 +#define CP0SRSC4_SRS13	0 +    int32_t CP0_HWREna; +    target_ulong CP0_BadVAddr; +    uint32_t CP0_BadInstr; +    uint32_t CP0_BadInstrP; +    int32_t CP0_Count; +    target_ulong CP0_EntryHi; +#define CP0EnHi_EHINV 10 +    int32_t CP0_Compare; +    int32_t CP0_Status; +#define CP0St_CU3   31 +#define CP0St_CU2   30 +#define CP0St_CU1   29 +#define CP0St_CU0   28 +#define CP0St_RP    27 +#define CP0St_FR    26 +#define CP0St_RE    25 +#define CP0St_MX    24 +#define CP0St_PX    23 +#define CP0St_BEV   22 +#define CP0St_TS    21 +#define CP0St_SR    20 +#define CP0St_NMI   19 +#define CP0St_IM    8 +#define CP0St_KX    7 +#define CP0St_SX    6 +#define CP0St_UX    5 +#define CP0St_KSU   3 +#define CP0St_ERL   2 +#define CP0St_EXL   1 +#define CP0St_IE    0 +    int32_t CP0_IntCtl; +#define CP0IntCtl_IPTI 29 +#define CP0IntCtl_IPPC1 26 +#define CP0IntCtl_VS 5 +    int32_t CP0_SRSCtl; +#define CP0SRSCtl_HSS 26 +#define CP0SRSCtl_EICSS 18 +#define CP0SRSCtl_ESS 12 +#define CP0SRSCtl_PSS 6 +#define CP0SRSCtl_CSS 0 +    int32_t CP0_SRSMap; +#define CP0SRSMap_SSV7 28 +#define CP0SRSMap_SSV6 24 +#define CP0SRSMap_SSV5 20 +#define CP0SRSMap_SSV4 16 +#define CP0SRSMap_SSV3 12 +#define CP0SRSMap_SSV2 8 +#define CP0SRSMap_SSV1 4 +#define CP0SRSMap_SSV0 0 +    int32_t CP0_Cause; +#define CP0Ca_BD   31 +#define CP0Ca_TI   30 +#define CP0Ca_CE   28 +#define CP0Ca_DC   27 +#define CP0Ca_PCI  26 +#define CP0Ca_IV   23 +#define CP0Ca_WP   22 +#define CP0Ca_IP    8 +#define CP0Ca_IP_mask 0x0000FF00 +#define CP0Ca_EC    2 +    target_ulong CP0_EPC; +    int32_t CP0_PRid; +    int32_t CP0_EBase; +    int32_t CP0_Config0; +#define CP0C0_M    31 +#define CP0C0_K23  28 +#define CP0C0_KU   25 +#define CP0C0_MDU  20 +#define CP0C0_MM   17 +#define CP0C0_BM   16 +#define CP0C0_BE   15 +#define CP0C0_AT   13 +#define CP0C0_AR   10 +#define CP0C0_MT   7 +#define CP0C0_VI   3 +#define CP0C0_K0   0 +    int32_t CP0_Config1; +#define CP0C1_M    31 +#define CP0C1_MMU  25 +#define CP0C1_IS   22 +#define CP0C1_IL   19 +#define CP0C1_IA   16 +#define CP0C1_DS   13 +#define CP0C1_DL   10 +#define CP0C1_DA   7 +#define CP0C1_C2   6 +#define CP0C1_MD   5 +#define CP0C1_PC   4 +#define CP0C1_WR   3 +#define CP0C1_CA   2 +#define CP0C1_EP   1 +#define CP0C1_FP   0 +    int32_t CP0_Config2; +#define CP0C2_M    31 +#define CP0C2_TU   28 +#define CP0C2_TS   24 +#define CP0C2_TL   20 +#define CP0C2_TA   16 +#define CP0C2_SU   12 +#define CP0C2_SS   8 +#define CP0C2_SL   4 +#define CP0C2_SA   0 +    int32_t CP0_Config3; +#define CP0C3_M    31 +#define CP0C3_BPG  30 +#define CP0C3_CMCGR 29 +#define CP0C3_MSAP  28 +#define CP0C3_BP 27 +#define CP0C3_BI 26 +#define CP0C3_IPLW 21 +#define CP0C3_MMAR 18 +#define CP0C3_MCU  17 +#define CP0C3_ISA_ON_EXC 16 +#define CP0C3_ISA  14 +#define CP0C3_ULRI 13 +#define CP0C3_RXI  12 +#define CP0C3_DSP2P 11 +#define CP0C3_DSPP 10 +#define CP0C3_LPA  7 +#define CP0C3_VEIC 6 +#define CP0C3_VInt 5 +#define CP0C3_SP   4 +#define CP0C3_CDMM 3 +#define CP0C3_MT   2 +#define CP0C3_SM   1 +#define CP0C3_TL   0 +    int32_t CP0_Config4; +    int32_t CP0_Config4_rw_bitmask; +#define CP0C4_M    31 +#define CP0C4_IE   29 +#define CP0C4_KScrExist 16 +#define CP0C4_MMUExtDef 14 +#define CP0C4_FTLBPageSize 8 +#define CP0C4_FTLBWays 4 +#define CP0C4_FTLBSets 0 +#define CP0C4_MMUSizeExt 0 +    int32_t CP0_Config5; +    int32_t CP0_Config5_rw_bitmask; +#define CP0C5_M          31 +#define CP0C5_K          30 +#define CP0C5_CV         29 +#define CP0C5_EVA        28 +#define CP0C5_MSAEn      27 +#define CP0C5_UFE        9 +#define CP0C5_FRE        8 +#define CP0C5_SBRI       6 +#define CP0C5_MVH        5 +#define CP0C5_LLB        4 +#define CP0C5_UFR        2 +#define CP0C5_NFExists   0 +    int32_t CP0_Config6; +    int32_t CP0_Config7; +    /* XXX: Maybe make LLAddr per-TC? */ +    uint64_t lladdr; +    target_ulong llval; +    target_ulong llnewval; +    target_ulong llreg; +    uint64_t CP0_LLAddr_rw_bitmask; +    int CP0_LLAddr_shift; +    target_ulong CP0_WatchLo[8]; +    int32_t CP0_WatchHi[8]; +    target_ulong CP0_XContext; +    int32_t CP0_Framemask; +    int32_t CP0_Debug; +#define CP0DB_DBD  31 +#define CP0DB_DM   30 +#define CP0DB_LSNM 28 +#define CP0DB_Doze 27 +#define CP0DB_Halt 26 +#define CP0DB_CNT  25 +#define CP0DB_IBEP 24 +#define CP0DB_DBEP 21 +#define CP0DB_IEXI 20 +#define CP0DB_VER  15 +#define CP0DB_DEC  10 +#define CP0DB_SSt  8 +#define CP0DB_DINT 5 +#define CP0DB_DIB  4 +#define CP0DB_DDBS 3 +#define CP0DB_DDBL 2 +#define CP0DB_DBp  1 +#define CP0DB_DSS  0 +    target_ulong CP0_DEPC; +    int32_t CP0_Performance0; +    uint64_t CP0_TagLo; +    int32_t CP0_DataLo; +    int32_t CP0_TagHi; +    int32_t CP0_DataHi; +    target_ulong CP0_ErrorEPC; +    int32_t CP0_DESAVE; +    /* We waste some space so we can handle shadow registers like TCs. */ +    TCState tcs[MIPS_SHADOW_SET_MAX]; +    CPUMIPSFPUContext fpus[MIPS_FPU_MAX]; +    /* QEMU */ +    int error_code; +#define EXCP_TLB_NOMATCH   0x1 +#define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */ +    uint32_t hflags;    /* CPU State */ +    /* TMASK defines different execution modes */ +#define MIPS_HFLAG_TMASK  0x75807FF +#define MIPS_HFLAG_MODE   0x00007 /* execution modes                    */ +    /* The KSU flags must be the lowest bits in hflags. The flag order +       must be the same as defined for CP0 Status. This allows to use +       the bits as the value of mmu_idx. */ +#define MIPS_HFLAG_KSU    0x00003 /* kernel/supervisor/user mode mask   */ +#define MIPS_HFLAG_UM     0x00002 /* user mode flag                     */ +#define MIPS_HFLAG_SM     0x00001 /* supervisor mode flag               */ +#define MIPS_HFLAG_KM     0x00000 /* kernel mode flag                   */ +#define MIPS_HFLAG_DM     0x00004 /* Debug mode                         */ +#define MIPS_HFLAG_64     0x00008 /* 64-bit instructions enabled        */ +#define MIPS_HFLAG_CP0    0x00010 /* CP0 enabled                        */ +#define MIPS_HFLAG_FPU    0x00020 /* FPU enabled                        */ +#define MIPS_HFLAG_F64    0x00040 /* 64-bit FPU enabled                 */ +    /* True if the MIPS IV COP1X instructions can be used.  This also +       controls the non-COP1X instructions RECIP.S, RECIP.D, RSQRT.S +       and RSQRT.D.  */ +#define MIPS_HFLAG_COP1X  0x00080 /* COP1X instructions enabled         */ +#define MIPS_HFLAG_RE     0x00100 /* Reversed endianness                */ +#define MIPS_HFLAG_AWRAP  0x00200 /* 32-bit compatibility address wrapping */ +#define MIPS_HFLAG_M16    0x00400 /* MIPS16 mode flag                   */ +#define MIPS_HFLAG_M16_SHIFT 10 +    /* If translation is interrupted between the branch instruction and +     * the delay slot, record what type of branch it is so that we can +     * resume translation properly.  It might be possible to reduce +     * this from three bits to two.  */ +#define MIPS_HFLAG_BMASK_BASE  0x803800 +#define MIPS_HFLAG_B      0x00800 /* Unconditional branch               */ +#define MIPS_HFLAG_BC     0x01000 /* Conditional branch                 */ +#define MIPS_HFLAG_BL     0x01800 /* Likely branch                      */ +#define MIPS_HFLAG_BR     0x02000 /* branch to register (can't link TB) */ +    /* Extra flags about the current pending branch.  */ +#define MIPS_HFLAG_BMASK_EXT 0x7C000 +#define MIPS_HFLAG_B16    0x04000 /* branch instruction was 16 bits     */ +#define MIPS_HFLAG_BDS16  0x08000 /* branch requires 16-bit delay slot  */ +#define MIPS_HFLAG_BDS32  0x10000 /* branch requires 32-bit delay slot  */ +#define MIPS_HFLAG_BDS_STRICT  0x20000 /* Strict delay slot size */ +#define MIPS_HFLAG_BX     0x40000 /* branch exchanges execution mode    */ +#define MIPS_HFLAG_BMASK  (MIPS_HFLAG_BMASK_BASE | MIPS_HFLAG_BMASK_EXT) +    /* MIPS DSP resources access. */ +#define MIPS_HFLAG_DSP   0x080000  /* Enable access to MIPS DSP resources. */ +#define MIPS_HFLAG_DSPR2 0x100000  /* Enable access to MIPS DSPR2 resources. */ +    /* Extra flag about HWREna register. */ +#define MIPS_HFLAG_HWRENA_ULR 0x200000 /* ULR bit from HWREna is set. */ +#define MIPS_HFLAG_SBRI  0x400000 /* R6 SDBBP causes RI excpt. in user mode */ +#define MIPS_HFLAG_FBNSLOT 0x800000 /* Forbidden slot                   */ +#define MIPS_HFLAG_MSA   0x1000000 +#define MIPS_HFLAG_FRE   0x2000000 /* FRE enabled */ +#define MIPS_HFLAG_ELPA  0x4000000 +    target_ulong btarget;        /* Jump / branch target               */ +    target_ulong bcond;          /* Branch condition (if needed)       */ + +    int SYNCI_Step; /* Address step size for SYNCI */ +    int CCRes; /* Cycle count resolution/divisor */ +    uint32_t CP0_Status_rw_bitmask; /* Read/write bits in CP0_Status */ +    uint32_t CP0_TCStatus_rw_bitmask; /* Read/write bits in CP0_TCStatus */ +    int insn_flags; /* Supported instruction set */ + +    CPU_COMMON + +    /* Fields from here on are preserved across CPU reset. */ +    CPUMIPSMVPContext *mvp; +#if !defined(CONFIG_USER_ONLY) +    CPUMIPSTLBContext *tlb; +#endif + +    const mips_def_t *cpu_model; +    void *irq[8]; +    QEMUTimer *timer; /* Internal timer */ +}; + +#include "cpu-qom.h" + +#if !defined(CONFIG_USER_ONLY) +int no_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, +                        target_ulong address, int rw, int access_type); +int fixed_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, +                           target_ulong address, int rw, int access_type); +int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, +                     target_ulong address, int rw, int access_type); +void r4k_helper_tlbwi(CPUMIPSState *env); +void r4k_helper_tlbwr(CPUMIPSState *env); +void r4k_helper_tlbp(CPUMIPSState *env); +void r4k_helper_tlbr(CPUMIPSState *env); +void r4k_helper_tlbinv(CPUMIPSState *env); +void r4k_helper_tlbinvf(CPUMIPSState *env); + +void mips_cpu_unassigned_access(CPUState *cpu, hwaddr addr, +                                bool is_write, bool is_exec, int unused, +                                unsigned size); +#endif + +void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf); + +#define cpu_exec cpu_mips_exec +#define cpu_gen_code cpu_mips_gen_code +#define cpu_signal_handler cpu_mips_signal_handler +#define cpu_list mips_cpu_list + +extern void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env); +extern uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env); + +/* MMU modes definitions. We carefully match the indices with our +   hflags layout. */ +#define MMU_MODE0_SUFFIX _kernel +#define MMU_MODE1_SUFFIX _super +#define MMU_MODE2_SUFFIX _user +#define MMU_USER_IDX 2 +static inline int cpu_mmu_index (CPUMIPSState *env) +{ +    return env->hflags & MIPS_HFLAG_KSU; +} + +static inline int cpu_mips_hw_interrupts_pending(CPUMIPSState *env) +{ +    int32_t pending; +    int32_t status; +    int r; + +    if (!(env->CP0_Status & (1 << CP0St_IE)) || +        (env->CP0_Status & (1 << CP0St_EXL)) || +        (env->CP0_Status & (1 << CP0St_ERL)) || +        /* Note that the TCStatus IXMT field is initialized to zero, +           and only MT capable cores can set it to one. So we don't +           need to check for MT capabilities here.  */ +        (env->active_tc.CP0_TCStatus & (1 << CP0TCSt_IXMT)) || +        (env->hflags & MIPS_HFLAG_DM)) { +        /* Interrupts are disabled */ +        return 0; +    } + +    pending = env->CP0_Cause & CP0Ca_IP_mask; +    status = env->CP0_Status & CP0Ca_IP_mask; + +    if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { +        /* A MIPS configured with a vectorizing external interrupt controller +           will feed a vector into the Cause pending lines. The core treats +           the status lines as a vector level, not as indiviual masks.  */ +        r = pending > status; +    } else { +        /* A MIPS configured with compatibility or VInt (Vectored Interrupts) +           treats the pending lines as individual interrupt lines, the status +           lines are individual masks.  */ +        r = pending & status; +    } +    return r; +} + +#include "exec/cpu-all.h" + +/* Memory access type : + * may be needed for precise access rights control and precise exceptions. + */ +enum { +    /* 1 bit to define user level / supervisor access */ +    ACCESS_USER  = 0x00, +    ACCESS_SUPER = 0x01, +    /* 1 bit to indicate direction */ +    ACCESS_STORE = 0x02, +    /* Type of instruction that generated the access */ +    ACCESS_CODE  = 0x10, /* Code fetch access                */ +    ACCESS_INT   = 0x20, /* Integer load/store access        */ +    ACCESS_FLOAT = 0x30, /* floating point load/store access */ +}; + +/* Exceptions */ +enum { +    EXCP_NONE          = -1, +    EXCP_RESET         = 0, +    EXCP_SRESET, +    EXCP_DSS, +    EXCP_DINT, +    EXCP_DDBL, +    EXCP_DDBS, +    EXCP_NMI, +    EXCP_MCHECK, +    EXCP_EXT_INTERRUPT, /* 8 */ +    EXCP_DFWATCH, +    EXCP_DIB, +    EXCP_IWATCH, +    EXCP_AdEL, +    EXCP_AdES, +    EXCP_TLBF, +    EXCP_IBE, +    EXCP_DBp, /* 16 */ +    EXCP_SYSCALL, +    EXCP_BREAK, +    EXCP_CpU, +    EXCP_RI, +    EXCP_OVERFLOW, +    EXCP_TRAP, +    EXCP_FPE, +    EXCP_DWATCH, /* 24 */ +    EXCP_LTLBL, +    EXCP_TLBL, +    EXCP_TLBS, +    EXCP_DBE, +    EXCP_THREAD, +    EXCP_MDMX, +    EXCP_C2E, +    EXCP_CACHE, /* 32 */ +    EXCP_DSPDIS, +    EXCP_MSADIS, +    EXCP_MSAFPE, +    EXCP_TLBXI, +    EXCP_TLBRI, + +    EXCP_LAST = EXCP_TLBRI, +}; +/* Dummy exception for conditional stores.  */ +#define EXCP_SC 0x100 + +/* + * This is an interrnally generated WAKE request line. + * It is driven by the CPU itself. Raised when the MT + * block wants to wake a VPE from an inactive state and + * cleared when VPE goes from active to inactive. + */ +#define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0 + +int cpu_mips_exec(CPUState *cpu); +void mips_tcg_init(void); +MIPSCPU *cpu_mips_init(const char *cpu_model); +int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc); + +#define cpu_init(cpu_model) CPU(cpu_mips_init(cpu_model)) + +/* TODO QOM'ify CPU reset and remove */ +void cpu_state_reset(CPUMIPSState *s); + +/* mips_timer.c */ +uint32_t cpu_mips_get_random (CPUMIPSState *env); +uint32_t cpu_mips_get_count (CPUMIPSState *env); +void cpu_mips_store_count (CPUMIPSState *env, uint32_t value); +void cpu_mips_store_compare (CPUMIPSState *env, uint32_t value); +void cpu_mips_start_count(CPUMIPSState *env); +void cpu_mips_stop_count(CPUMIPSState *env); + +/* mips_int.c */ +void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level); + +/* helper.c */ +int mips_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, +                              int mmu_idx); +#if !defined(CONFIG_USER_ONLY) +void r4k_invalidate_tlb (CPUMIPSState *env, int idx, int use_extra); +hwaddr cpu_mips_translate_address (CPUMIPSState *env, target_ulong address, +		                               int rw); +#endif +target_ulong exception_resume_pc (CPUMIPSState *env); + +/* op_helper.c */ +extern unsigned int ieee_rm[]; +int ieee_ex_to_mips(int xcpt); + +static inline void restore_rounding_mode(CPUMIPSState *env) +{ +    set_float_rounding_mode(ieee_rm[env->active_fpu.fcr31 & 3], +                            &env->active_fpu.fp_status); +} + +static inline void restore_flush_mode(CPUMIPSState *env) +{ +    set_flush_to_zero((env->active_fpu.fcr31 & (1 << 24)) != 0, +                      &env->active_fpu.fp_status); +} + +static inline void restore_fp_status(CPUMIPSState *env) +{ +    restore_rounding_mode(env); +    restore_flush_mode(env); +} + +static inline void restore_msa_fp_status(CPUMIPSState *env) +{ +    float_status *status = &env->active_tc.msa_fp_status; +    int rounding_mode = (env->active_tc.msacsr & MSACSR_RM_MASK) >> MSACSR_RM; +    bool flush_to_zero = (env->active_tc.msacsr & MSACSR_FS_MASK) != 0; + +    set_float_rounding_mode(ieee_rm[rounding_mode], status); +    set_flush_to_zero(flush_to_zero, status); +    set_flush_inputs_to_zero(flush_to_zero, status); +} + +static inline void restore_pamask(CPUMIPSState *env) +{ +    if (env->hflags & MIPS_HFLAG_ELPA) { +        env->PAMask = (1ULL << env->PABITS) - 1; +    } else { +        env->PAMask = PAMASK_BASE; +    } +} + +static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc, +                                        target_ulong *cs_base, int *flags) +{ +    *pc = env->active_tc.PC; +    *cs_base = 0; +    *flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK | +                            MIPS_HFLAG_HWRENA_ULR); +} + +static inline int mips_vpe_active(CPUMIPSState *env) +{ +    int active = 1; + +    /* Check that the VPE is enabled.  */ +    if (!(env->mvp->CP0_MVPControl & (1 << CP0MVPCo_EVP))) { +        active = 0; +    } +    /* Check that the VPE is activated.  */ +    if (!(env->CP0_VPEConf0 & (1 << CP0VPEC0_VPA))) { +        active = 0; +    } + +    /* Now verify that there are active thread contexts in the VPE. + +       This assumes the CPU model will internally reschedule threads +       if the active one goes to sleep. If there are no threads available +       the active one will be in a sleeping state, and we can turn off +       the entire VPE.  */ +    if (!(env->active_tc.CP0_TCStatus & (1 << CP0TCSt_A))) { +        /* TC is not activated.  */ +        active = 0; +    } +    if (env->active_tc.CP0_TCHalt & 1) { +        /* TC is in halt state.  */ +        active = 0; +    } + +    return active; +} + +#include "exec/exec-all.h" + +static inline void compute_hflags(CPUMIPSState *env) +{ +    env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | +                     MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU | +                     MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 | +                     MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE | +                     MIPS_HFLAG_ELPA); +    if (!(env->CP0_Status & (1 << CP0St_EXL)) && +        !(env->CP0_Status & (1 << CP0St_ERL)) && +        !(env->hflags & MIPS_HFLAG_DM)) { +        env->hflags |= (env->CP0_Status >> CP0St_KSU) & MIPS_HFLAG_KSU; +    } +#if defined(TARGET_MIPS64) +    if ((env->insn_flags & ISA_MIPS3) && +        (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_UM) || +         (env->CP0_Status & (1 << CP0St_PX)) || +         (env->CP0_Status & (1 << CP0St_UX)))) { +        env->hflags |= MIPS_HFLAG_64; +    } + +    if (!(env->insn_flags & ISA_MIPS3)) { +        env->hflags |= MIPS_HFLAG_AWRAP; +    } else if (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_UM) && +               !(env->CP0_Status & (1 << CP0St_UX))) { +        env->hflags |= MIPS_HFLAG_AWRAP; +    } else if (env->insn_flags & ISA_MIPS64R6) { +        /* Address wrapping for Supervisor and Kernel is specified in R6 */ +        if ((((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_SM) && +             !(env->CP0_Status & (1 << CP0St_SX))) || +            (((env->hflags & MIPS_HFLAG_KSU) == MIPS_HFLAG_KM) && +             !(env->CP0_Status & (1 << CP0St_KX)))) { +            env->hflags |= MIPS_HFLAG_AWRAP; +        } +    } +#endif +    if (((env->CP0_Status & (1 << CP0St_CU0)) && +         !(env->insn_flags & ISA_MIPS32R6)) || +        !(env->hflags & MIPS_HFLAG_KSU)) { +        env->hflags |= MIPS_HFLAG_CP0; +    } +    if (env->CP0_Status & (1 << CP0St_CU1)) { +        env->hflags |= MIPS_HFLAG_FPU; +    } +    if (env->CP0_Status & (1 << CP0St_FR)) { +        env->hflags |= MIPS_HFLAG_F64; +    } +    if (((env->hflags & MIPS_HFLAG_KSU) != MIPS_HFLAG_KM) && +        (env->CP0_Config5 & (1 << CP0C5_SBRI))) { +        env->hflags |= MIPS_HFLAG_SBRI; +    } +    if (env->insn_flags & ASE_DSPR2) { +        /* Enables access MIPS DSP resources, now our cpu is DSP ASER2, +           so enable to access DSPR2 resources. */ +        if (env->CP0_Status & (1 << CP0St_MX)) { +            env->hflags |= MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2; +        } + +    } else if (env->insn_flags & ASE_DSP) { +        /* Enables access MIPS DSP resources, now our cpu is DSP ASE, +           so enable to access DSP resources. */ +        if (env->CP0_Status & (1 << CP0St_MX)) { +            env->hflags |= MIPS_HFLAG_DSP; +        } + +    } +    if (env->insn_flags & ISA_MIPS32R2) { +        if (env->active_fpu.fcr0 & (1 << FCR0_F64)) { +            env->hflags |= MIPS_HFLAG_COP1X; +        } +    } else if (env->insn_flags & ISA_MIPS32) { +        if (env->hflags & MIPS_HFLAG_64) { +            env->hflags |= MIPS_HFLAG_COP1X; +        } +    } else if (env->insn_flags & ISA_MIPS4) { +        /* All supported MIPS IV CPUs use the XX (CU3) to enable +           and disable the MIPS IV extensions to the MIPS III ISA. +           Some other MIPS IV CPUs ignore the bit, so the check here +           would be too restrictive for them.  */ +        if (env->CP0_Status & (1U << CP0St_CU3)) { +            env->hflags |= MIPS_HFLAG_COP1X; +        } +    } +    if (env->insn_flags & ASE_MSA) { +        if (env->CP0_Config5 & (1 << CP0C5_MSAEn)) { +            env->hflags |= MIPS_HFLAG_MSA; +        } +    } +    if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { +        if (env->CP0_Config5 & (1 << CP0C5_FRE)) { +            env->hflags |= MIPS_HFLAG_FRE; +        } +    } +    if (env->CP0_Config3 & (1 << CP0C3_LPA)) { +        if (env->CP0_PageGrain & (1 << CP0PG_ELPA)) { +            env->hflags |= MIPS_HFLAG_ELPA; +        } +    } +} + +#ifndef CONFIG_USER_ONLY +/* Called for updates to CP0_Status.  */ +static inline void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc) +{ +    int32_t tcstatus, *tcst; +    uint32_t v = cpu->CP0_Status; +    uint32_t cu, mx, asid, ksu; +    uint32_t mask = ((1 << CP0TCSt_TCU3) +                       | (1 << CP0TCSt_TCU2) +                       | (1 << CP0TCSt_TCU1) +                       | (1 << CP0TCSt_TCU0) +                       | (1 << CP0TCSt_TMX) +                       | (3 << CP0TCSt_TKSU) +                       | (0xff << CP0TCSt_TASID)); + +    cu = (v >> CP0St_CU0) & 0xf; +    mx = (v >> CP0St_MX) & 0x1; +    ksu = (v >> CP0St_KSU) & 0x3; +    asid = env->CP0_EntryHi & 0xff; + +    tcstatus = cu << CP0TCSt_TCU0; +    tcstatus |= mx << CP0TCSt_TMX; +    tcstatus |= ksu << CP0TCSt_TKSU; +    tcstatus |= asid; + +    if (tc == cpu->current_tc) { +        tcst = &cpu->active_tc.CP0_TCStatus; +    } else { +        tcst = &cpu->tcs[tc].CP0_TCStatus; +    } + +    *tcst &= ~mask; +    *tcst |= tcstatus; +    compute_hflags(cpu); +} + +static inline void cpu_mips_store_status(CPUMIPSState *env, target_ulong val) +{ +    uint32_t mask = env->CP0_Status_rw_bitmask; + +    if (env->insn_flags & ISA_MIPS32R6) { +        bool has_supervisor = extract32(mask, CP0St_KSU, 2) == 0x3; + +        if (has_supervisor && extract32(val, CP0St_KSU, 2) == 0x3) { +            mask &= ~(3 << CP0St_KSU); +        } +        mask &= ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & val); +    } + +    env->CP0_Status = (env->CP0_Status & ~mask) | (val & mask); +    if (env->CP0_Config3 & (1 << CP0C3_MT)) { +        sync_c0_status(env, env, env->current_tc); +    } else { +        compute_hflags(env); +    } +} + +static inline void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val) +{ +    uint32_t mask = 0x00C00300; +    uint32_t old = env->CP0_Cause; +    int i; + +    if (env->insn_flags & ISA_MIPS32R2) { +        mask |= 1 << CP0Ca_DC; +    } +    if (env->insn_flags & ISA_MIPS32R6) { +        mask &= ~((1 << CP0Ca_WP) & val); +    } + +    env->CP0_Cause = (env->CP0_Cause & ~mask) | (val & mask); + +    if ((old ^ env->CP0_Cause) & (1 << CP0Ca_DC)) { +        if (env->CP0_Cause & (1 << CP0Ca_DC)) { +            cpu_mips_stop_count(env); +        } else { +            cpu_mips_start_count(env); +        } +    } + +    /* Set/reset software interrupts */ +    for (i = 0 ; i < 2 ; i++) { +        if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) { +            cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i))); +        } +    } +} +#endif + +#endif /* !defined (__MIPS_CPU_H__) */ diff --git a/target-mips/dsp_helper.c b/target-mips/dsp_helper.c new file mode 100644 index 00000000..46528de3 --- /dev/null +++ b/target-mips/dsp_helper.c @@ -0,0 +1,3761 @@ +/* + * MIPS ASE DSP Instruction emulation helpers for QEMU. + * + * Copyright (c) 2012  Jia Liu <proljc@gmail.com> + *                     Dongxue Zhang <elta.era@gmail.com> + * 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 "cpu.h" +#include "exec/helper-proto.h" +#include "qemu/bitops.h" + +/* As the byte ordering doesn't matter, i.e. all columns are treated +   identically, these unions can be used directly.  */ +typedef union { +    uint8_t  ub[4]; +    int8_t   sb[4]; +    uint16_t uh[2]; +    int16_t  sh[2]; +    uint32_t uw[1]; +    int32_t  sw[1]; +} DSP32Value; + +typedef union { +    uint8_t  ub[8]; +    int8_t   sb[8]; +    uint16_t uh[4]; +    int16_t  sh[4]; +    uint32_t uw[2]; +    int32_t  sw[2]; +    uint64_t ul[1]; +    int64_t  sl[1]; +} DSP64Value; + +/*** MIPS DSP internal functions begin ***/ +#define MIPSDSP_ABS(x) (((x) >= 0) ? x : -x) +#define MIPSDSP_OVERFLOW_ADD(a, b, c, d) (~(a ^ b) & (a ^ c) & d) +#define MIPSDSP_OVERFLOW_SUB(a, b, c, d) ((a ^ b) & (a ^ c) & d) + +static inline void set_DSPControl_overflow_flag(uint32_t flag, int position, +                                                CPUMIPSState *env) +{ +    env->active_tc.DSPControl |= (target_ulong)flag << position; +} + +static inline void set_DSPControl_carryflag(bool flag, CPUMIPSState *env) +{ +    env->active_tc.DSPControl &= ~(1 << 13); +    env->active_tc.DSPControl |= flag << 13; +} + +static inline uint32_t get_DSPControl_carryflag(CPUMIPSState *env) +{ +    return (env->active_tc.DSPControl >> 13) & 0x01; +} + +static inline void set_DSPControl_24(uint32_t flag, int len, CPUMIPSState *env) +{ +  uint32_t filter; + +  filter = ((0x01 << len) - 1) << 24; +  filter = ~filter; + +  env->active_tc.DSPControl &= filter; +  env->active_tc.DSPControl |= (target_ulong)flag << 24; +} + +static inline void set_DSPControl_pos(uint32_t pos, CPUMIPSState *env) +{ +    target_ulong dspc; + +    dspc = env->active_tc.DSPControl; +#ifndef TARGET_MIPS64 +    dspc = dspc & 0xFFFFFFC0; +    dspc |= (pos & 0x3F); +#else +    dspc = dspc & 0xFFFFFF80; +    dspc |= (pos & 0x7F); +#endif +    env->active_tc.DSPControl = dspc; +} + +static inline uint32_t get_DSPControl_pos(CPUMIPSState *env) +{ +    target_ulong dspc; +    uint32_t pos; + +    dspc = env->active_tc.DSPControl; + +#ifndef TARGET_MIPS64 +    pos = dspc & 0x3F; +#else +    pos = dspc & 0x7F; +#endif + +    return pos; +} + +static inline void set_DSPControl_efi(uint32_t flag, CPUMIPSState *env) +{ +    env->active_tc.DSPControl &= 0xFFFFBFFF; +    env->active_tc.DSPControl |= (target_ulong)flag << 14; +} + +#define DO_MIPS_SAT_ABS(size)                                          \ +static inline int##size##_t mipsdsp_sat_abs##size(int##size##_t a,         \ +                                                  CPUMIPSState *env)   \ +{                                                                      \ +    if (a == INT##size##_MIN) {                                        \ +        set_DSPControl_overflow_flag(1, 20, env);                      \ +        return INT##size##_MAX;                                        \ +    } else {                                                           \ +        return MIPSDSP_ABS(a);                                         \ +    }                                                                  \ +} +DO_MIPS_SAT_ABS(8) +DO_MIPS_SAT_ABS(16) +DO_MIPS_SAT_ABS(32) +#undef DO_MIPS_SAT_ABS + +/* get sum value */ +static inline int16_t mipsdsp_add_i16(int16_t a, int16_t b, CPUMIPSState *env) +{ +    int16_t tempI; + +    tempI = a + b; + +    if (MIPSDSP_OVERFLOW_ADD(a, b, tempI, 0x8000)) { +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return tempI; +} + +static inline int16_t mipsdsp_sat_add_i16(int16_t a, int16_t b, +                                          CPUMIPSState *env) +{ +    int16_t tempS; + +    tempS = a + b; + +    if (MIPSDSP_OVERFLOW_ADD(a, b, tempS, 0x8000)) { +        if (a > 0) { +            tempS = 0x7FFF; +        } else { +            tempS = 0x8000; +        } +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return tempS; +} + +static inline int32_t mipsdsp_sat_add_i32(int32_t a, int32_t b, +                                          CPUMIPSState *env) +{ +    int32_t tempI; + +    tempI = a + b; + +    if (MIPSDSP_OVERFLOW_ADD(a, b, tempI, 0x80000000)) { +        if (a > 0) { +            tempI = 0x7FFFFFFF; +        } else { +            tempI = 0x80000000; +        } +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return tempI; +} + +static inline uint8_t mipsdsp_add_u8(uint8_t a, uint8_t b, CPUMIPSState *env) +{ +    uint16_t temp; + +    temp = (uint16_t)a + (uint16_t)b; + +    if (temp & 0x0100) { +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return temp & 0xFF; +} + +static inline uint16_t mipsdsp_add_u16(uint16_t a, uint16_t b, +                                       CPUMIPSState *env) +{ +    uint32_t temp; + +    temp = (uint32_t)a + (uint32_t)b; + +    if (temp & 0x00010000) { +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return temp & 0xFFFF; +} + +static inline uint8_t mipsdsp_sat_add_u8(uint8_t a, uint8_t b, +                                         CPUMIPSState *env) +{ +    uint8_t  result; +    uint16_t temp; + +    temp = (uint16_t)a + (uint16_t)b; +    result = temp & 0xFF; + +    if (0x0100 & temp) { +        result = 0xFF; +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return result; +} + +static inline uint16_t mipsdsp_sat_add_u16(uint16_t a, uint16_t b, +                                           CPUMIPSState *env) +{ +    uint16_t result; +    uint32_t temp; + +    temp = (uint32_t)a + (uint32_t)b; +    result = temp & 0xFFFF; + +    if (0x00010000 & temp) { +        result = 0xFFFF; +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return result; +} + +static inline int32_t mipsdsp_sat32_acc_q31(int32_t acc, int32_t a, +                                            CPUMIPSState *env) +{ +    int64_t temp; +    int32_t temp32, temp31, result; +    int64_t temp_sum; + +#ifndef TARGET_MIPS64 +    temp = ((uint64_t)env->active_tc.HI[acc] << 32) | +           (uint64_t)env->active_tc.LO[acc]; +#else +    temp = (uint64_t)env->active_tc.LO[acc]; +#endif + +    temp_sum = (int64_t)a + temp; + +    temp32 = (temp_sum >> 32) & 0x01; +    temp31 = (temp_sum >> 31) & 0x01; +    result = temp_sum & 0xFFFFFFFF; + +    if (temp32 != temp31) { +        if (temp32 == 0) { +            result = 0x7FFFFFFF; +        } else { +            result = 0x80000000; +        } +        set_DSPControl_overflow_flag(1, 16 + acc, env); +    } + +    return result; +} + +#ifdef TARGET_MIPS64 +/* a[0] is LO, a[1] is HI. */ +static inline void mipsdsp_sat64_acc_add_q63(int64_t *ret, +                                             int32_t ac, +                                             int64_t *a, +                                             CPUMIPSState *env) +{ +    bool temp64; + +    ret[0] = env->active_tc.LO[ac] + a[0]; +    ret[1] = env->active_tc.HI[ac] + a[1]; + +    if (((uint64_t)ret[0] < (uint64_t)env->active_tc.LO[ac]) && +        ((uint64_t)ret[0] < (uint64_t)a[0])) { +        ret[1] += 1; +    } +    temp64 = ret[1] & 1; +    if (temp64 != ((ret[0] >> 63) & 0x01)) { +        if (temp64) { +            ret[0] = (0x01ull << 63); +            ret[1] = ~0ull; +        } else { +            ret[0] = (0x01ull << 63) - 1; +            ret[1] = 0x00; +        } +        set_DSPControl_overflow_flag(1, 16 + ac, env); +    } +} + +static inline void mipsdsp_sat64_acc_sub_q63(int64_t *ret, +                                             int32_t ac, +                                             int64_t *a, +                                             CPUMIPSState *env) +{ +    bool temp64; + +    ret[0] = env->active_tc.LO[ac] - a[0]; +    ret[1] = env->active_tc.HI[ac] - a[1]; + +    if ((uint64_t)ret[0] > (uint64_t)env->active_tc.LO[ac]) { +        ret[1] -= 1; +    } +    temp64 = ret[1] & 1; +    if (temp64 != ((ret[0] >> 63) & 0x01)) { +        if (temp64) { +            ret[0] = (0x01ull << 63); +            ret[1] = ~0ull; +        } else { +            ret[0] = (0x01ull << 63) - 1; +            ret[1] = 0x00; +        } +        set_DSPControl_overflow_flag(1, 16 + ac, env); +    } +} +#endif + +static inline int32_t mipsdsp_mul_i16_i16(int16_t a, int16_t b, +                                          CPUMIPSState *env) +{ +    int32_t temp; + +    temp = (int32_t)a * (int32_t)b; + +    if ((temp > (int)0x7FFF) || (temp < (int)0xFFFF8000)) { +        set_DSPControl_overflow_flag(1, 21, env); +    } +    temp &= 0x0000FFFF; + +    return temp; +} + +static inline int32_t mipsdsp_mul_u16_u16(int32_t a, int32_t b) +{ +    return a * b; +} + +#ifdef TARGET_MIPS64 +static inline int32_t mipsdsp_mul_i32_i32(int32_t a, int32_t b) +{ +    return a * b; +} +#endif + +static inline int32_t mipsdsp_sat16_mul_i16_i16(int16_t a, int16_t b, +                                                CPUMIPSState *env) +{ +    int32_t temp; + +    temp = (int32_t)a * (int32_t)b; + +    if (temp > (int)0x7FFF) { +        temp = 0x00007FFF; +        set_DSPControl_overflow_flag(1, 21, env); +    } else if (temp < (int)0xffff8000) { +        temp = 0xFFFF8000; +        set_DSPControl_overflow_flag(1, 21, env); +    } +    temp &= 0x0000FFFF; + +    return temp; +} + +static inline int32_t mipsdsp_mul_q15_q15_overflowflag21(uint16_t a, uint16_t b, +                                                         CPUMIPSState *env) +{ +    int32_t temp; + +    if ((a == 0x8000) && (b == 0x8000)) { +        temp = 0x7FFFFFFF; +        set_DSPControl_overflow_flag(1, 21, env); +    } else { +        temp = ((int16_t)a * (int16_t)b) << 1; +    } + +    return temp; +} + +/* right shift */ +static inline uint8_t mipsdsp_rshift_u8(uint8_t a, target_ulong mov) +{ +    return a >> mov; +} + +static inline uint16_t mipsdsp_rshift_u16(uint16_t a, target_ulong mov) +{ +    return a >> mov; +} + +static inline int8_t mipsdsp_rashift8(int8_t a, target_ulong mov) +{ +    return a >> mov; +} + +static inline int16_t mipsdsp_rashift16(int16_t a, target_ulong mov) +{ +    return a >> mov; +} + +#ifdef TARGET_MIPS64 +static inline int32_t mipsdsp_rashift32(int32_t a, target_ulong mov) +{ +    return a >> mov; +} +#endif + +static inline int16_t mipsdsp_rshift1_add_q16(int16_t a, int16_t b) +{ +    int32_t temp; + +    temp = (int32_t)a + (int32_t)b; + +    return (temp >> 1) & 0xFFFF; +} + +/* round right shift */ +static inline int16_t mipsdsp_rrshift1_add_q16(int16_t a, int16_t b) +{ +    int32_t temp; + +    temp = (int32_t)a + (int32_t)b; +    temp += 1; + +    return (temp >> 1) & 0xFFFF; +} + +static inline int32_t mipsdsp_rshift1_add_q32(int32_t a, int32_t b) +{ +    int64_t temp; + +    temp = (int64_t)a + (int64_t)b; + +    return (temp >> 1) & 0xFFFFFFFF; +} + +static inline int32_t mipsdsp_rrshift1_add_q32(int32_t a, int32_t b) +{ +    int64_t temp; + +    temp = (int64_t)a + (int64_t)b; +    temp += 1; + +    return (temp >> 1) & 0xFFFFFFFF; +} + +static inline uint8_t mipsdsp_rshift1_add_u8(uint8_t a, uint8_t b) +{ +    uint16_t temp; + +    temp = (uint16_t)a + (uint16_t)b; + +    return (temp >> 1) & 0x00FF; +} + +static inline uint8_t mipsdsp_rrshift1_add_u8(uint8_t a, uint8_t b) +{ +    uint16_t temp; + +    temp = (uint16_t)a + (uint16_t)b + 1; + +    return (temp >> 1) & 0x00FF; +} + +#ifdef TARGET_MIPS64 +static inline uint8_t mipsdsp_rshift1_sub_u8(uint8_t a, uint8_t b) +{ +    uint16_t temp; + +    temp = (uint16_t)a - (uint16_t)b; + +    return (temp >> 1) & 0x00FF; +} + +static inline uint8_t mipsdsp_rrshift1_sub_u8(uint8_t a, uint8_t b) +{ +    uint16_t temp; + +    temp = (uint16_t)a - (uint16_t)b + 1; + +    return (temp >> 1) & 0x00FF; +} +#endif + +/*  128 bits long. p[0] is LO, p[1] is HI. */ +static inline void mipsdsp_rndrashift_short_acc(int64_t *p, +                                                int32_t ac, +                                                int32_t shift, +                                                CPUMIPSState *env) +{ +    int64_t acc; + +    acc = ((int64_t)env->active_tc.HI[ac] << 32) | +          ((int64_t)env->active_tc.LO[ac] & 0xFFFFFFFF); +    p[0] = (shift == 0) ? (acc << 1) : (acc >> (shift - 1)); +    p[1] = (acc >> 63) & 0x01; +} + +#ifdef TARGET_MIPS64 +/* 128 bits long. p[0] is LO, p[1] is HI */ +static inline void mipsdsp_rashift_acc(uint64_t *p, +                                       uint32_t ac, +                                       uint32_t shift, +                                       CPUMIPSState *env) +{ +    uint64_t tempB, tempA; + +    tempB = env->active_tc.HI[ac]; +    tempA = env->active_tc.LO[ac]; +    shift = shift & 0x1F; + +    if (shift == 0) { +        p[1] = tempB; +        p[0] = tempA; +    } else { +        p[0] = (tempB << (64 - shift)) | (tempA >> shift); +        p[1] = (int64_t)tempB >> shift; +    } +} + +/* 128 bits long. p[0] is LO, p[1] is HI , p[2] is sign of HI.*/ +static inline void mipsdsp_rndrashift_acc(uint64_t *p, +                                          uint32_t ac, +                                          uint32_t shift, +                                          CPUMIPSState *env) +{ +    int64_t tempB, tempA; + +    tempB = env->active_tc.HI[ac]; +    tempA = env->active_tc.LO[ac]; +    shift = shift & 0x3F; + +    if (shift == 0) { +        p[2] = tempB >> 63; +        p[1] = (tempB << 1) | (tempA >> 63); +        p[0] = tempA << 1; +    } else { +        p[0] = (tempB << (65 - shift)) | (tempA >> (shift - 1)); +        p[1] = (int64_t)tempB >> (shift - 1); +        if (tempB >= 0) { +            p[2] = 0x0; +        } else { +            p[2] = ~0ull; +        } +    } +} +#endif + +static inline int32_t mipsdsp_mul_q15_q15(int32_t ac, uint16_t a, uint16_t b, +                                          CPUMIPSState *env) +{ +    int32_t temp; + +    if ((a == 0x8000) && (b == 0x8000)) { +        temp = 0x7FFFFFFF; +        set_DSPControl_overflow_flag(1, 16 + ac, env); +    } else { +        temp = ((int16_t)a * (int16_t)b) << 1; +    } + +    return temp; +} + +static inline int64_t mipsdsp_mul_q31_q31(int32_t ac, uint32_t a, uint32_t b, +                                          CPUMIPSState *env) +{ +    uint64_t temp; + +    if ((a == 0x80000000) && (b == 0x80000000)) { +        temp = (0x01ull << 63) - 1; +        set_DSPControl_overflow_flag(1, 16 + ac, env); +    } else { +        temp = ((int64_t)(int32_t)a * (int32_t)b) << 1; +    } + +    return temp; +} + +static inline uint16_t mipsdsp_mul_u8_u8(uint8_t a, uint8_t b) +{ +    return (uint16_t)a * (uint16_t)b; +} + +static inline uint16_t mipsdsp_mul_u8_u16(uint8_t a, uint16_t b, +                                          CPUMIPSState *env) +{ +    uint32_t tempI; + +    tempI = (uint32_t)a * (uint32_t)b; +    if (tempI > 0x0000FFFF) { +        tempI = 0x0000FFFF; +        set_DSPControl_overflow_flag(1, 21, env); +    } + +    return tempI & 0x0000FFFF; +} + +#ifdef TARGET_MIPS64 +static inline uint64_t mipsdsp_mul_u32_u32(uint32_t a, uint32_t b) +{ +    return (uint64_t)a * (uint64_t)b; +} +#endif + +static inline int16_t mipsdsp_rndq15_mul_q15_q15(uint16_t a, uint16_t b, +                                                 CPUMIPSState *env) +{ +    uint32_t temp; + +    if ((a == 0x8000) && (b == 0x8000)) { +        temp = 0x7FFF0000; +        set_DSPControl_overflow_flag(1, 21, env); +    } else { +        temp = ((int16_t)a * (int16_t)b) << 1; +        temp = temp + 0x00008000; +    } + +    return (temp & 0xFFFF0000) >> 16; +} + +static inline int32_t mipsdsp_sat16_mul_q15_q15(uint16_t a, uint16_t b, +                                                CPUMIPSState *env) +{ +    int32_t temp; + +    if ((a == 0x8000) && (b == 0x8000)) { +        temp = 0x7FFF0000; +        set_DSPControl_overflow_flag(1, 21, env); +    } else { +        temp = (int16_t)a * (int16_t)b; +        temp = temp << 1; +    } + +    return (temp >> 16) & 0x0000FFFF; +} + +static inline uint16_t mipsdsp_trunc16_sat16_round(int32_t a, +                                                   CPUMIPSState *env) +{ +    uint16_t temp; + + +    /* +     * The value 0x00008000 will be added to the input Q31 value, and the code +     * needs to check if the addition causes an overflow. Since a positive value +     * is added, overflow can happen in one direction only. +     */ +    if (a > 0x7FFF7FFF) { +        temp = 0x7FFF; +        set_DSPControl_overflow_flag(1, 22, env); +    } else { +        temp = ((a + 0x8000) >> 16) & 0xFFFF; +    } + +    return temp; +} + +static inline uint8_t mipsdsp_sat8_reduce_precision(uint16_t a, +                                                    CPUMIPSState *env) +{ +    uint16_t mag; +    uint32_t sign; + +    sign = (a >> 15) & 0x01; +    mag = a & 0x7FFF; + +    if (sign == 0) { +        if (mag > 0x7F80) { +            set_DSPControl_overflow_flag(1, 22, env); +            return 0xFF; +        } else { +            return (mag >> 7) & 0xFFFF; +        } +    } else { +        set_DSPControl_overflow_flag(1, 22, env); +        return 0x00; +    } +} + +static inline uint8_t mipsdsp_lshift8(uint8_t a, uint8_t s, CPUMIPSState *env) +{ +    uint8_t discard; + +    if (s != 0) { +        discard = a >> (8 - s); + +        if (discard != 0x00) { +            set_DSPControl_overflow_flag(1, 22, env); +        } +    } +    return a << s; +} + +static inline uint16_t mipsdsp_lshift16(uint16_t a, uint8_t s, +                                        CPUMIPSState *env) +{ +    uint16_t discard; + +    if (s != 0) { +        discard = (int16_t)a >> (15 - s); + +        if ((discard != 0x0000) && (discard != 0xFFFF)) { +            set_DSPControl_overflow_flag(1, 22, env); +        } +    } +    return a << s; +} + +#ifdef TARGET_MIPS64 +static inline uint32_t mipsdsp_lshift32(uint32_t a, uint8_t s, +                                        CPUMIPSState *env) +{ +    uint32_t discard; + +    if (s == 0) { +        return a; +    } else { +        discard = (int32_t)a >> (31 - (s - 1)); + +        if ((discard != 0x00000000) && (discard != 0xFFFFFFFF)) { +            set_DSPControl_overflow_flag(1, 22, env); +        } +        return a << s; +    } +} +#endif + +static inline uint16_t mipsdsp_sat16_lshift(uint16_t a, uint8_t s, +                                            CPUMIPSState *env) +{ +    uint8_t  sign; +    uint16_t discard; + +    if (s == 0) { +        return a; +    } else { +        sign = (a >> 15) & 0x01; +        if (sign != 0) { +            discard = (((0x01 << (16 - s)) - 1) << s) | +                      ((a >> (14 - (s - 1))) & ((0x01 << s) - 1)); +        } else { +            discard = a >> (14 - (s - 1)); +        } + +        if ((discard != 0x0000) && (discard != 0xFFFF)) { +            set_DSPControl_overflow_flag(1, 22, env); +            return (sign == 0) ? 0x7FFF : 0x8000; +        } else { +            return a << s; +        } +    } +} + +static inline uint32_t mipsdsp_sat32_lshift(uint32_t a, uint8_t s, +                                            CPUMIPSState *env) +{ +    uint8_t  sign; +    uint32_t discard; + +    if (s == 0) { +        return a; +    } else { +        sign = (a >> 31) & 0x01; +        if (sign != 0) { +            discard = (((0x01 << (32 - s)) - 1) << s) | +                      ((a >> (30 - (s - 1))) & ((0x01 << s) - 1)); +        } else { +            discard = a >> (30 - (s - 1)); +        } + +        if ((discard != 0x00000000) && (discard != 0xFFFFFFFF)) { +            set_DSPControl_overflow_flag(1, 22, env); +            return (sign == 0) ? 0x7FFFFFFF : 0x80000000; +        } else { +            return a << s; +        } +    } +} + +static inline uint8_t mipsdsp_rnd8_rashift(uint8_t a, uint8_t s) +{ +    uint32_t temp; + +    if (s == 0) { +        temp = (uint32_t)a << 1; +    } else { +        temp = (int32_t)(int8_t)a >> (s - 1); +    } + +    return (temp + 1) >> 1; +} + +static inline uint16_t mipsdsp_rnd16_rashift(uint16_t a, uint8_t s) +{ +    uint32_t temp; + +    if (s == 0) { +        temp = (uint32_t)a << 1; +    } else { +        temp = (int32_t)(int16_t)a >> (s - 1); +    } + +    return (temp + 1) >> 1; +} + +static inline uint32_t mipsdsp_rnd32_rashift(uint32_t a, uint8_t s) +{ +    int64_t temp; + +    if (s == 0) { +        temp = (uint64_t)a << 1; +    } else { +        temp = (int64_t)(int32_t)a >> (s - 1); +    } +    temp += 1; + +    return (temp >> 1) & 0xFFFFFFFFull; +} + +static inline uint16_t mipsdsp_sub_i16(int16_t a, int16_t b, CPUMIPSState *env) +{ +    int16_t  temp; + +    temp = a - b; +    if (MIPSDSP_OVERFLOW_SUB(a, b, temp, 0x8000)) { +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return temp; +} + +static inline uint16_t mipsdsp_sat16_sub(int16_t a, int16_t b, +                                         CPUMIPSState *env) +{ +    int16_t  temp; + +    temp = a - b; +    if (MIPSDSP_OVERFLOW_SUB(a, b, temp, 0x8000)) { +        if (a >= 0) { +            temp = 0x7FFF; +        } else { +            temp = 0x8000; +        } +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return temp; +} + +static inline uint32_t mipsdsp_sat32_sub(int32_t a, int32_t b, +                                         CPUMIPSState *env) +{ +    int32_t  temp; + +    temp = a - b; +    if (MIPSDSP_OVERFLOW_SUB(a, b, temp, 0x80000000)) { +        if (a >= 0) { +            temp = 0x7FFFFFFF; +        } else { +            temp = 0x80000000; +        } +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return temp & 0xFFFFFFFFull; +} + +static inline uint16_t mipsdsp_rshift1_sub_q16(int16_t a, int16_t b) +{ +    int32_t  temp; + +    temp = (int32_t)a - (int32_t)b; + +    return (temp >> 1) & 0x0000FFFF; +} + +static inline uint16_t mipsdsp_rrshift1_sub_q16(int16_t a, int16_t b) +{ +    int32_t  temp; + +    temp = (int32_t)a - (int32_t)b; +    temp += 1; + +    return (temp >> 1) & 0x0000FFFF; +} + +static inline uint32_t mipsdsp_rshift1_sub_q32(int32_t a, int32_t b) +{ +    int64_t  temp; + +    temp = (int64_t)a - (int64_t)b; + +    return (temp >> 1) & 0xFFFFFFFFull; +} + +static inline uint32_t mipsdsp_rrshift1_sub_q32(int32_t a, int32_t b) +{ +    int64_t  temp; + +    temp = (int64_t)a - (int64_t)b; +    temp += 1; + +    return (temp >> 1) & 0xFFFFFFFFull; +} + +static inline uint16_t mipsdsp_sub_u16_u16(uint16_t a, uint16_t b, +                                           CPUMIPSState *env) +{ +    uint8_t  temp16; +    uint32_t temp; + +    temp = (uint32_t)a - (uint32_t)b; +    temp16 = (temp >> 16) & 0x01; +    if (temp16 == 1) { +        set_DSPControl_overflow_flag(1, 20, env); +    } +    return temp & 0x0000FFFF; +} + +static inline uint16_t mipsdsp_satu16_sub_u16_u16(uint16_t a, uint16_t b, +                                                  CPUMIPSState *env) +{ +    uint8_t  temp16; +    uint32_t temp; + +    temp   = (uint32_t)a - (uint32_t)b; +    temp16 = (temp >> 16) & 0x01; + +    if (temp16 == 1) { +        temp = 0x0000; +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return temp & 0x0000FFFF; +} + +static inline uint8_t mipsdsp_sub_u8(uint8_t a, uint8_t b, CPUMIPSState *env) +{ +    uint8_t  temp8; +    uint16_t temp; + +    temp = (uint16_t)a - (uint16_t)b; +    temp8 = (temp >> 8) & 0x01; +    if (temp8 == 1) { +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return temp & 0x00FF; +} + +static inline uint8_t mipsdsp_satu8_sub(uint8_t a, uint8_t b, CPUMIPSState *env) +{ +    uint8_t  temp8; +    uint16_t temp; + +    temp = (uint16_t)a - (uint16_t)b; +    temp8 = (temp >> 8) & 0x01; +    if (temp8 == 1) { +        temp = 0x00; +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return temp & 0x00FF; +} + +#ifdef TARGET_MIPS64 +static inline uint32_t mipsdsp_sub32(int32_t a, int32_t b, CPUMIPSState *env) +{ +    int32_t temp; + +    temp = a - b; +    if (MIPSDSP_OVERFLOW_SUB(a, b, temp, 0x80000000)) { +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return temp; +} + +static inline int32_t mipsdsp_add_i32(int32_t a, int32_t b, CPUMIPSState *env) +{ +    int32_t temp; + +    temp = a + b; + +    if (MIPSDSP_OVERFLOW_ADD(a, b, temp, 0x80000000)) { +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    return temp; +} +#endif + +static inline int32_t mipsdsp_cmp_eq(int32_t a, int32_t b) +{ +    return a == b; +} + +static inline int32_t mipsdsp_cmp_le(int32_t a, int32_t b) +{ +    return a <= b; +} + +static inline int32_t mipsdsp_cmp_lt(int32_t a, int32_t b) +{ +    return a < b; +} + +static inline int32_t mipsdsp_cmpu_eq(uint32_t a, uint32_t b) +{ +    return a == b; +} + +static inline int32_t mipsdsp_cmpu_le(uint32_t a, uint32_t b) +{ +    return a <= b; +} + +static inline int32_t mipsdsp_cmpu_lt(uint32_t a, uint32_t b) +{ +    return a < b; +} +/*** MIPS DSP internal functions end ***/ + +#define MIPSDSP_LHI 0xFFFFFFFF00000000ull +#define MIPSDSP_LLO 0x00000000FFFFFFFFull +#define MIPSDSP_HI  0xFFFF0000 +#define MIPSDSP_LO  0x0000FFFF +#define MIPSDSP_Q3  0xFF000000 +#define MIPSDSP_Q2  0x00FF0000 +#define MIPSDSP_Q1  0x0000FF00 +#define MIPSDSP_Q0  0x000000FF + +#define MIPSDSP_SPLIT32_8(num, a, b, c, d)  \ +    do {                                    \ +        a = (num >> 24) & MIPSDSP_Q0;       \ +        b = (num >> 16) & MIPSDSP_Q0;       \ +        c = (num >> 8) & MIPSDSP_Q0;        \ +        d = num & MIPSDSP_Q0;               \ +    } while (0) + +#define MIPSDSP_SPLIT32_16(num, a, b)       \ +    do {                                    \ +        a = (num >> 16) & MIPSDSP_LO;       \ +        b = num & MIPSDSP_LO;               \ +    } while (0) + +#define MIPSDSP_RETURN32_8(a, b, c, d)  ((target_long)(int32_t) \ +                                         (((uint32_t)a << 24) | \ +                                         (((uint32_t)b << 16) | \ +                                         (((uint32_t)c << 8) |  \ +                                          ((uint32_t)d & 0xFF))))) +#define MIPSDSP_RETURN32_16(a, b)       ((target_long)(int32_t) \ +                                         (((uint32_t)a << 16) | \ +                                          ((uint32_t)b & 0xFFFF))) + +#ifdef TARGET_MIPS64 +#define MIPSDSP_SPLIT64_16(num, a, b, c, d)  \ +    do {                                     \ +        a = (num >> 48) & MIPSDSP_LO;        \ +        b = (num >> 32) & MIPSDSP_LO;        \ +        c = (num >> 16) & MIPSDSP_LO;        \ +        d = num & MIPSDSP_LO;                \ +    } while (0) + +#define MIPSDSP_SPLIT64_32(num, a, b)       \ +    do {                                    \ +        a = (num >> 32) & MIPSDSP_LLO;      \ +        b = num & MIPSDSP_LLO;              \ +    } while (0) + +#define MIPSDSP_RETURN64_16(a, b, c, d) (((uint64_t)a << 48) | \ +                                         ((uint64_t)b << 32) | \ +                                         ((uint64_t)c << 16) | \ +                                         (uint64_t)d) +#define MIPSDSP_RETURN64_32(a, b)       (((uint64_t)a << 32) | (uint64_t)b) +#endif + +/** DSP Arithmetic Sub-class insns **/ +#define MIPSDSP32_UNOP_ENV(name, func, element)                            \ +target_ulong helper_##name(target_ulong rt, CPUMIPSState *env)             \ +{                                                                          \ +    DSP32Value dt;                                                         \ +    unsigned int i;                                                     \ +                                                                           \ +    dt.sw[0] = rt;                                                         \ +                                                                           \ +    for (i = 0; i < ARRAY_SIZE(dt.element); i++) {                         \ +        dt.element[i] = mipsdsp_##func(dt.element[i], env);                \ +    }                                                                      \ +                                                                           \ +    return (target_long)dt.sw[0];                                          \ +} +MIPSDSP32_UNOP_ENV(absq_s_ph, sat_abs16, sh) +MIPSDSP32_UNOP_ENV(absq_s_qb, sat_abs8, sb) +MIPSDSP32_UNOP_ENV(absq_s_w, sat_abs32, sw) +#undef MIPSDSP32_UNOP_ENV + +#if defined(TARGET_MIPS64) +#define MIPSDSP64_UNOP_ENV(name, func, element)                            \ +target_ulong helper_##name(target_ulong rt, CPUMIPSState *env)             \ +{                                                                          \ +    DSP64Value dt;                                                         \ +    unsigned int i;                                                        \ +                                                                           \ +    dt.sl[0] = rt;                                                         \ +                                                                           \ +    for (i = 0; i < ARRAY_SIZE(dt.element); i++) {                         \ +        dt.element[i] = mipsdsp_##func(dt.element[i], env);                \ +    }                                                                      \ +                                                                           \ +    return dt.sl[0];                                                       \ +} +MIPSDSP64_UNOP_ENV(absq_s_ob, sat_abs8, sb) +MIPSDSP64_UNOP_ENV(absq_s_qh, sat_abs16, sh) +MIPSDSP64_UNOP_ENV(absq_s_pw, sat_abs32, sw) +#undef MIPSDSP64_UNOP_ENV +#endif + +#define MIPSDSP32_BINOP(name, func, element)                               \ +target_ulong helper_##name(target_ulong rs, target_ulong rt)               \ +{                                                                          \ +    DSP32Value ds, dt;                                                     \ +    unsigned int i;                                                        \ +                                                                           \ +    ds.sw[0] = rs;                                                         \ +    dt.sw[0] = rt;                                                         \ +                                                                           \ +    for (i = 0; i < ARRAY_SIZE(ds.element); i++) {                         \ +        ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i]);      \ +    }                                                                      \ +                                                                           \ +    return (target_long)ds.sw[0];                                          \ +} +MIPSDSP32_BINOP(addqh_ph, rshift1_add_q16, sh); +MIPSDSP32_BINOP(addqh_r_ph, rrshift1_add_q16, sh); +MIPSDSP32_BINOP(addqh_r_w, rrshift1_add_q32, sw); +MIPSDSP32_BINOP(addqh_w, rshift1_add_q32, sw); +MIPSDSP32_BINOP(adduh_qb, rshift1_add_u8, ub); +MIPSDSP32_BINOP(adduh_r_qb, rrshift1_add_u8, ub); +MIPSDSP32_BINOP(subqh_ph, rshift1_sub_q16, sh); +MIPSDSP32_BINOP(subqh_r_ph, rrshift1_sub_q16, sh); +MIPSDSP32_BINOP(subqh_r_w, rrshift1_sub_q32, sw); +MIPSDSP32_BINOP(subqh_w, rshift1_sub_q32, sw); +#undef MIPSDSP32_BINOP + +#define MIPSDSP32_BINOP_ENV(name, func, element)                           \ +target_ulong helper_##name(target_ulong rs, target_ulong rt,               \ +                           CPUMIPSState *env)                              \ +{                                                                          \ +    DSP32Value ds, dt;                                                     \ +    unsigned int i;                                                        \ +                                                                           \ +    ds.sw[0] = rs;                                                         \ +    dt.sw[0] = rt;                                                         \ +                                                                           \ +    for (i = 0 ; i < ARRAY_SIZE(ds.element); i++) {                        \ +        ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i], env); \ +    }                                                                      \ +                                                                           \ +    return (target_long)ds.sw[0];                                          \ +} +MIPSDSP32_BINOP_ENV(addq_ph, add_i16, sh) +MIPSDSP32_BINOP_ENV(addq_s_ph, sat_add_i16, sh) +MIPSDSP32_BINOP_ENV(addq_s_w, sat_add_i32, sw); +MIPSDSP32_BINOP_ENV(addu_ph, add_u16, sh) +MIPSDSP32_BINOP_ENV(addu_qb, add_u8, ub); +MIPSDSP32_BINOP_ENV(addu_s_ph, sat_add_u16, sh) +MIPSDSP32_BINOP_ENV(addu_s_qb, sat_add_u8, ub); +MIPSDSP32_BINOP_ENV(subq_ph, sub_i16, sh); +MIPSDSP32_BINOP_ENV(subq_s_ph, sat16_sub, sh); +MIPSDSP32_BINOP_ENV(subq_s_w, sat32_sub, sw); +MIPSDSP32_BINOP_ENV(subu_ph, sub_u16_u16, sh); +MIPSDSP32_BINOP_ENV(subu_qb, sub_u8, ub); +MIPSDSP32_BINOP_ENV(subu_s_ph, satu16_sub_u16_u16, sh); +MIPSDSP32_BINOP_ENV(subu_s_qb, satu8_sub, ub); +#undef MIPSDSP32_BINOP_ENV + +#ifdef TARGET_MIPS64 +#define MIPSDSP64_BINOP(name, func, element)                               \ +target_ulong helper_##name(target_ulong rs, target_ulong rt)               \ +{                                                                          \ +    DSP64Value ds, dt;                                                     \ +    unsigned int i;                                                        \ +                                                                           \ +    ds.sl[0] = rs;                                                         \ +    dt.sl[0] = rt;                                                         \ +                                                                           \ +    for (i = 0 ; i < ARRAY_SIZE(ds.element); i++) {                        \ +        ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i]);      \ +    }                                                                      \ +                                                                           \ +    return ds.sl[0];                                                       \ +} +MIPSDSP64_BINOP(adduh_ob, rshift1_add_u8, ub); +MIPSDSP64_BINOP(adduh_r_ob, rrshift1_add_u8, ub); +MIPSDSP64_BINOP(subuh_ob, rshift1_sub_u8, ub); +MIPSDSP64_BINOP(subuh_r_ob, rrshift1_sub_u8, ub); +#undef MIPSDSP64_BINOP + +#define MIPSDSP64_BINOP_ENV(name, func, element)                           \ +target_ulong helper_##name(target_ulong rs, target_ulong rt,               \ +                           CPUMIPSState *env)                              \ +{                                                                          \ +    DSP64Value ds, dt;                                                     \ +    unsigned int i;                                                        \ +                                                                           \ +    ds.sl[0] = rs;                                                         \ +    dt.sl[0] = rt;                                                         \ +                                                                           \ +    for (i = 0 ; i < ARRAY_SIZE(ds.element); i++) {                        \ +        ds.element[i] = mipsdsp_##func(ds.element[i], dt.element[i], env); \ +    }                                                                      \ +                                                                           \ +    return ds.sl[0];                                                       \ +} +MIPSDSP64_BINOP_ENV(addq_pw, add_i32, sw); +MIPSDSP64_BINOP_ENV(addq_qh, add_i16, sh); +MIPSDSP64_BINOP_ENV(addq_s_pw, sat_add_i32, sw); +MIPSDSP64_BINOP_ENV(addq_s_qh, sat_add_i16, sh); +MIPSDSP64_BINOP_ENV(addu_ob, add_u8, uh); +MIPSDSP64_BINOP_ENV(addu_qh, add_u16, uh); +MIPSDSP64_BINOP_ENV(addu_s_ob, sat_add_u8, uh); +MIPSDSP64_BINOP_ENV(addu_s_qh, sat_add_u16, uh); +MIPSDSP64_BINOP_ENV(subq_pw, sub32, sw); +MIPSDSP64_BINOP_ENV(subq_qh, sub_i16, sh); +MIPSDSP64_BINOP_ENV(subq_s_pw, sat32_sub, sw); +MIPSDSP64_BINOP_ENV(subq_s_qh, sat16_sub, sh); +MIPSDSP64_BINOP_ENV(subu_ob, sub_u8, uh); +MIPSDSP64_BINOP_ENV(subu_qh, sub_u16_u16, uh); +MIPSDSP64_BINOP_ENV(subu_s_ob, satu8_sub, uh); +MIPSDSP64_BINOP_ENV(subu_s_qh, satu16_sub_u16_u16, uh); +#undef MIPSDSP64_BINOP_ENV + +#endif + +#define SUBUH_QB(name, var) \ +target_ulong helper_##name##_qb(target_ulong rs, target_ulong rt) \ +{                                                                 \ +    uint8_t rs3, rs2, rs1, rs0;                                   \ +    uint8_t rt3, rt2, rt1, rt0;                                   \ +    uint8_t tempD, tempC, tempB, tempA;                           \ +                                                                  \ +    MIPSDSP_SPLIT32_8(rs, rs3, rs2, rs1, rs0);                    \ +    MIPSDSP_SPLIT32_8(rt, rt3, rt2, rt1, rt0);                    \ +                                                                  \ +    tempD = ((uint16_t)rs3 - (uint16_t)rt3 + var) >> 1;           \ +    tempC = ((uint16_t)rs2 - (uint16_t)rt2 + var) >> 1;           \ +    tempB = ((uint16_t)rs1 - (uint16_t)rt1 + var) >> 1;           \ +    tempA = ((uint16_t)rs0 - (uint16_t)rt0 + var) >> 1;           \ +                                                                  \ +    return ((uint32_t)tempD << 24) | ((uint32_t)tempC << 16) |    \ +        ((uint32_t)tempB << 8) | ((uint32_t)tempA);               \ +} + +SUBUH_QB(subuh, 0); +SUBUH_QB(subuh_r, 1); + +#undef SUBUH_QB + +target_ulong helper_addsc(target_ulong rs, target_ulong rt, CPUMIPSState *env) +{ +    uint64_t temp, tempRs, tempRt; +    bool flag; + +    tempRs = (uint64_t)rs & MIPSDSP_LLO; +    tempRt = (uint64_t)rt & MIPSDSP_LLO; + +    temp = tempRs + tempRt; +    flag = (temp & 0x0100000000ull) >> 32; +    set_DSPControl_carryflag(flag, env); + +    return (target_long)(int32_t)(temp & MIPSDSP_LLO); +} + +target_ulong helper_addwc(target_ulong rs, target_ulong rt, CPUMIPSState *env) +{ +    uint32_t rd; +    int32_t temp32, temp31; +    int64_t tempL; + +    tempL = (int64_t)(int32_t)rs + (int64_t)(int32_t)rt + +        get_DSPControl_carryflag(env); +    temp31 = (tempL >> 31) & 0x01; +    temp32 = (tempL >> 32) & 0x01; + +    if (temp31 != temp32) { +        set_DSPControl_overflow_flag(1, 20, env); +    } + +    rd = tempL & MIPSDSP_LLO; + +    return (target_long)(int32_t)rd; +} + +target_ulong helper_modsub(target_ulong rs, target_ulong rt) +{ +    int32_t decr; +    uint16_t lastindex; +    target_ulong rd; + +    decr = rt & MIPSDSP_Q0; +    lastindex = (rt >> 8) & MIPSDSP_LO; + +    if ((rs & MIPSDSP_LLO) == 0x00000000) { +        rd = (target_ulong)lastindex; +    } else { +        rd = rs - decr; +    } + +    return rd; +} + +target_ulong helper_raddu_w_qb(target_ulong rs) +{ +    target_ulong ret = 0; +    DSP32Value ds; +    unsigned int i; + +    ds.uw[0] = rs; +    for (i = 0; i < 4; i++) { +        ret += ds.ub[i]; +    } +    return ret; +} + +#if defined(TARGET_MIPS64) +target_ulong helper_raddu_l_ob(target_ulong rs) +{ +    target_ulong ret = 0; +    DSP64Value ds; +    unsigned int i; + +    ds.ul[0] = rs; +    for (i = 0; i < 8; i++) { +        ret += ds.ub[i]; +    } +    return ret; +} +#endif + +#define PRECR_QB_PH(name, a, b)\ +target_ulong helper_##name##_qb_ph(target_ulong rs, target_ulong rt) \ +{                                                                    \ +    uint8_t tempD, tempC, tempB, tempA;                              \ +                                                                     \ +    tempD = (rs >> a) & MIPSDSP_Q0;                                  \ +    tempC = (rs >> b) & MIPSDSP_Q0;                                  \ +    tempB = (rt >> a) & MIPSDSP_Q0;                                  \ +    tempA = (rt >> b) & MIPSDSP_Q0;                                  \ +                                                                     \ +    return MIPSDSP_RETURN32_8(tempD, tempC, tempB, tempA);           \ +} + +PRECR_QB_PH(precr, 16, 0); +PRECR_QB_PH(precrq, 24, 8); + +#undef PRECR_QB_OH + +target_ulong helper_precr_sra_ph_w(uint32_t sa, target_ulong rs, +                                   target_ulong rt) +{ +    uint16_t tempB, tempA; + +    tempB = ((int32_t)rt >> sa) & MIPSDSP_LO; +    tempA = ((int32_t)rs >> sa) & MIPSDSP_LO; + +    return MIPSDSP_RETURN32_16(tempB, tempA); +} + +target_ulong helper_precr_sra_r_ph_w(uint32_t sa, +                                     target_ulong rs, target_ulong rt) +{ +    uint64_t tempB, tempA; + +    /* If sa = 0, then (sa - 1) = -1 will case shift error, so we need else. */ +    if (sa == 0) { +        tempB = (rt & MIPSDSP_LO) << 1; +        tempA = (rs & MIPSDSP_LO) << 1; +    } else { +        tempB = ((int32_t)rt >> (sa - 1)) + 1; +        tempA = ((int32_t)rs >> (sa - 1)) + 1; +    } +    rt = (((tempB >> 1) & MIPSDSP_LO) << 16) | ((tempA >> 1) & MIPSDSP_LO); + +    return (target_long)(int32_t)rt; +} + +target_ulong helper_precrq_ph_w(target_ulong rs, target_ulong rt) +{ +    uint16_t tempB, tempA; + +    tempB = (rs & MIPSDSP_HI) >> 16; +    tempA = (rt & MIPSDSP_HI) >> 16; + +    return MIPSDSP_RETURN32_16(tempB, tempA); +} + +target_ulong helper_precrq_rs_ph_w(target_ulong rs, target_ulong rt, +                                   CPUMIPSState *env) +{ +    uint16_t tempB, tempA; + +    tempB = mipsdsp_trunc16_sat16_round(rs, env); +    tempA = mipsdsp_trunc16_sat16_round(rt, env); + +    return MIPSDSP_RETURN32_16(tempB, tempA); +} + +#if defined(TARGET_MIPS64) +target_ulong helper_precr_ob_qh(target_ulong rs, target_ulong rt) +{ +    uint8_t rs6, rs4, rs2, rs0; +    uint8_t rt6, rt4, rt2, rt0; +    uint64_t temp; + +    rs6 = (rs >> 48) & MIPSDSP_Q0; +    rs4 = (rs >> 32) & MIPSDSP_Q0; +    rs2 = (rs >> 16) & MIPSDSP_Q0; +    rs0 = rs & MIPSDSP_Q0; +    rt6 = (rt >> 48) & MIPSDSP_Q0; +    rt4 = (rt >> 32) & MIPSDSP_Q0; +    rt2 = (rt >> 16) & MIPSDSP_Q0; +    rt0 = rt & MIPSDSP_Q0; + +    temp = ((uint64_t)rs6 << 56) | ((uint64_t)rs4 << 48) | +           ((uint64_t)rs2 << 40) | ((uint64_t)rs0 << 32) | +           ((uint64_t)rt6 << 24) | ((uint64_t)rt4 << 16) | +           ((uint64_t)rt2 << 8) | (uint64_t)rt0; + +    return temp; +} + +#define PRECR_QH_PW(name, var) \ +target_ulong helper_precr_##name##_qh_pw(target_ulong rs, target_ulong rt, \ +                                    uint32_t sa)                      \ +{                                                                     \ +    uint16_t rs3, rs2, rs1, rs0;                                      \ +    uint16_t rt3, rt2, rt1, rt0;                                      \ +    uint16_t tempD, tempC, tempB, tempA;                              \ +                                                                      \ +    MIPSDSP_SPLIT64_16(rs, rs3, rs2, rs1, rs0);                       \ +    MIPSDSP_SPLIT64_16(rt, rt3, rt2, rt1, rt0);                       \ +                                                                      \ +    /* When sa = 0, we use rt2, rt0, rs2, rs0;                        \ +     * when sa != 0, we use rt3, rt1, rs3, rs1. */                    \ +    if (sa == 0) {                                                    \ +        tempD = rt2 << var;                                           \ +        tempC = rt0 << var;                                           \ +        tempB = rs2 << var;                                           \ +        tempA = rs0 << var;                                           \ +    } else {                                                          \ +        tempD = (((int16_t)rt3 >> sa) + var) >> var;                  \ +        tempC = (((int16_t)rt1 >> sa) + var) >> var;                  \ +        tempB = (((int16_t)rs3 >> sa) + var) >> var;                  \ +        tempA = (((int16_t)rs1 >> sa) + var) >> var;                  \ +    }                                                                 \ +                                                                      \ +    return MIPSDSP_RETURN64_16(tempD, tempC, tempB, tempA);           \ +} + +PRECR_QH_PW(sra, 0); +PRECR_QH_PW(sra_r, 1); + +#undef PRECR_QH_PW + +target_ulong helper_precrq_ob_qh(target_ulong rs, target_ulong rt) +{ +    uint8_t rs6, rs4, rs2, rs0; +    uint8_t rt6, rt4, rt2, rt0; +    uint64_t temp; + +    rs6 = (rs >> 56) & MIPSDSP_Q0; +    rs4 = (rs >> 40) & MIPSDSP_Q0; +    rs2 = (rs >> 24) & MIPSDSP_Q0; +    rs0 = (rs >> 8) & MIPSDSP_Q0; +    rt6 = (rt >> 56) & MIPSDSP_Q0; +    rt4 = (rt >> 40) & MIPSDSP_Q0; +    rt2 = (rt >> 24) & MIPSDSP_Q0; +    rt0 = (rt >> 8) & MIPSDSP_Q0; + +    temp = ((uint64_t)rs6 << 56) | ((uint64_t)rs4 << 48) | +           ((uint64_t)rs2 << 40) | ((uint64_t)rs0 << 32) | +           ((uint64_t)rt6 << 24) | ((uint64_t)rt4 << 16) | +           ((uint64_t)rt2 << 8) | (uint64_t)rt0; + +    return temp; +} + +target_ulong helper_precrq_qh_pw(target_ulong rs, target_ulong rt) +{ +    uint16_t tempD, tempC, tempB, tempA; + +    tempD = (rs >> 48) & MIPSDSP_LO; +    tempC = (rs >> 16) & MIPSDSP_LO; +    tempB = (rt >> 48) & MIPSDSP_LO; +    tempA = (rt >> 16) & MIPSDSP_LO; + +    return MIPSDSP_RETURN64_16(tempD, tempC, tempB, tempA); +} + +target_ulong helper_precrq_rs_qh_pw(target_ulong rs, target_ulong rt, +                                    CPUMIPSState *env) +{ +    uint32_t rs2, rs0; +    uint32_t rt2, rt0; +    uint16_t tempD, tempC, tempB, tempA; + +    rs2 = (rs >> 32) & MIPSDSP_LLO; +    rs0 = rs & MIPSDSP_LLO; +    rt2 = (rt >> 32) & MIPSDSP_LLO; +    rt0 = rt & MIPSDSP_LLO; + +    tempD = mipsdsp_trunc16_sat16_round(rs2, env); +    tempC = mipsdsp_trunc16_sat16_round(rs0, env); +    tempB = mipsdsp_trunc16_sat16_round(rt2, env); +    tempA = mipsdsp_trunc16_sat16_round(rt0, env); + +    return MIPSDSP_RETURN64_16(tempD, tempC, tempB, tempA); +} + +target_ulong helper_precrq_pw_l(target_ulong rs, target_ulong rt) +{ +    uint32_t tempB, tempA; + +    tempB = (rs >> 32) & MIPSDSP_LLO; +    tempA = (rt >> 32) & MIPSDSP_LLO; + +    return MIPSDSP_RETURN64_32(tempB, tempA); +} +#endif + +target_ulong helper_precrqu_s_qb_ph(target_ulong rs, target_ulong rt, +                                    CPUMIPSState *env) +{ +    uint8_t  tempD, tempC, tempB, tempA; +    uint16_t rsh, rsl, rth, rtl; + +    rsh = (rs & MIPSDSP_HI) >> 16; +    rsl =  rs & MIPSDSP_LO; +    rth = (rt & MIPSDSP_HI) >> 16; +    rtl =  rt & MIPSDSP_LO; + +    tempD = mipsdsp_sat8_reduce_precision(rsh, env); +    tempC = mipsdsp_sat8_reduce_precision(rsl, env); +    tempB = mipsdsp_sat8_reduce_precision(rth, env); +    tempA = mipsdsp_sat8_reduce_precision(rtl, env); + +    return MIPSDSP_RETURN32_8(tempD, tempC, tempB, tempA); +} + +#if defined(TARGET_MIPS64) +target_ulong helper_precrqu_s_ob_qh(target_ulong rs, target_ulong rt, +                                    CPUMIPSState *env) +{ +    int i; +    uint16_t rs3, rs2, rs1, rs0; +    uint16_t rt3, rt2, rt1, rt0; +    uint8_t temp[8]; +    uint64_t result; + +    result = 0; + +    MIPSDSP_SPLIT64_16(rs, rs3, rs2, rs1, rs0); +    MIPSDSP_SPLIT64_16(rt, rt3, rt2, rt1, rt0); + +    temp[7] = mipsdsp_sat8_reduce_precision(rs3, env); +    temp[6] = mipsdsp_sat8_reduce_precision(rs2, env); +    temp[5] = mipsdsp_sat8_reduce_precision(rs1, env); +    temp[4] = mipsdsp_sat8_reduce_precision(rs0, env); +    temp[3] = mipsdsp_sat8_reduce_precision(rt3, env); +    temp[2] = mipsdsp_sat8_reduce_precision(rt2, env); +    temp[1] = mipsdsp_sat8_reduce_precision(rt1, env); +    temp[0] = mipsdsp_sat8_reduce_precision(rt0, env); + +    for (i = 0; i < 8; i++) { +        result |= (uint64_t)temp[i] << (8 * i); +    } + +    return result; +} + +#define PRECEQ_PW(name, a, b) \ +target_ulong helper_preceq_pw_##name(target_ulong rt) \ +{                                                       \ +    uint16_t tempB, tempA;                              \ +    uint32_t tempBI, tempAI;                            \ +                                                        \ +    tempB = (rt >> a) & MIPSDSP_LO;                     \ +    tempA = (rt >> b) & MIPSDSP_LO;                     \ +                                                        \ +    tempBI = (uint32_t)tempB << 16;                     \ +    tempAI = (uint32_t)tempA << 16;                     \ +                                                        \ +    return MIPSDSP_RETURN64_32(tempBI, tempAI);         \ +} + +PRECEQ_PW(qhl, 48, 32); +PRECEQ_PW(qhr, 16, 0); +PRECEQ_PW(qhla, 48, 16); +PRECEQ_PW(qhra, 32, 0); + +#undef PRECEQ_PW + +#endif + +#define PRECEQU_PH(name, a, b) \ +target_ulong helper_precequ_ph_##name(target_ulong rt) \ +{                                                        \ +    uint16_t tempB, tempA;                               \ +                                                         \ +    tempB = (rt >> a) & MIPSDSP_Q0;                      \ +    tempA = (rt >> b) & MIPSDSP_Q0;                      \ +                                                         \ +    tempB = tempB << 7;                                  \ +    tempA = tempA << 7;                                  \ +                                                         \ +    return MIPSDSP_RETURN32_16(tempB, tempA);            \ +} + +PRECEQU_PH(qbl, 24, 16); +PRECEQU_PH(qbr, 8, 0); +PRECEQU_PH(qbla, 24, 8); +PRECEQU_PH(qbra, 16, 0); + +#undef PRECEQU_PH + +#if defined(TARGET_MIPS64) +#define PRECEQU_QH(name, a, b, c, d) \ +target_ulong helper_precequ_qh_##name(target_ulong rt)       \ +{                                                            \ +    uint16_t tempD, tempC, tempB, tempA;                     \ +                                                             \ +    tempD = (rt >> a) & MIPSDSP_Q0;                          \ +    tempC = (rt >> b) & MIPSDSP_Q0;                          \ +    tempB = (rt >> c) & MIPSDSP_Q0;                          \ +    tempA = (rt >> d) & MIPSDSP_Q0;                          \ +                                                             \ +    tempD = tempD << 7;                                      \ +    tempC = tempC << 7;                                      \ +    tempB = tempB << 7;                                      \ +    tempA = tempA << 7;                                      \ +                                                             \ +    return MIPSDSP_RETURN64_16(tempD, tempC, tempB, tempA);  \ +} + +PRECEQU_QH(obl, 56, 48, 40, 32); +PRECEQU_QH(obr, 24, 16, 8, 0); +PRECEQU_QH(obla, 56, 40, 24, 8); +PRECEQU_QH(obra, 48, 32, 16, 0); + +#undef PRECEQU_QH + +#endif + +#define PRECEU_PH(name, a, b) \ +target_ulong helper_preceu_ph_##name(target_ulong rt) \ +{                                                     \ +    uint16_t tempB, tempA;                            \ +                                                      \ +    tempB = (rt >> a) & MIPSDSP_Q0;                   \ +    tempA = (rt >> b) & MIPSDSP_Q0;                   \ +                                                      \ +    return MIPSDSP_RETURN32_16(tempB, tempA);         \ +} + +PRECEU_PH(qbl, 24, 16); +PRECEU_PH(qbr, 8, 0); +PRECEU_PH(qbla, 24, 8); +PRECEU_PH(qbra, 16, 0); + +#undef PRECEU_PH + +#if defined(TARGET_MIPS64) +#define PRECEU_QH(name, a, b, c, d) \ +target_ulong helper_preceu_qh_##name(target_ulong rt)        \ +{                                                            \ +    uint16_t tempD, tempC, tempB, tempA;                     \ +                                                             \ +    tempD = (rt >> a) & MIPSDSP_Q0;                          \ +    tempC = (rt >> b) & MIPSDSP_Q0;                          \ +    tempB = (rt >> c) & MIPSDSP_Q0;                          \ +    tempA = (rt >> d) & MIPSDSP_Q0;                          \ +                                                             \ +    return MIPSDSP_RETURN64_16(tempD, tempC, tempB, tempA);  \ +} + +PRECEU_QH(obl, 56, 48, 40, 32); +PRECEU_QH(obr, 24, 16, 8, 0); +PRECEU_QH(obla, 56, 40, 24, 8); +PRECEU_QH(obra, 48, 32, 16, 0); + +#undef PRECEU_QH + +#endif + +/** DSP GPR-Based Shift Sub-class insns **/ +#define SHIFT_QB(name, func) \ +target_ulong helper_##name##_qb(target_ulong sa, target_ulong rt) \ +{                                                                    \ +    uint8_t rt3, rt2, rt1, rt0;                                      \ +                                                                     \ +    sa = sa & 0x07;                                                  \ +                                                                     \ +    MIPSDSP_SPLIT32_8(rt, rt3, rt2, rt1, rt0);                       \ +                                                                     \ +    rt3 = mipsdsp_##func(rt3, sa);                                   \ +    rt2 = mipsdsp_##func(rt2, sa);                                   \ +    rt1 = mipsdsp_##func(rt1, sa);                                   \ +    rt0 = mipsdsp_##func(rt0, sa);                                   \ +                                                                     \ +    return MIPSDSP_RETURN32_8(rt3, rt2, rt1, rt0);                   \ +} + +#define SHIFT_QB_ENV(name, func) \ +target_ulong helper_##name##_qb(target_ulong sa, target_ulong rt,\ +                                CPUMIPSState *env) \ +{                                                                    \ +    uint8_t rt3, rt2, rt1, rt0;                                      \ +                                                                     \ +    sa = sa & 0x07;                                                  \ +                                                                     \ +    MIPSDSP_SPLIT32_8(rt, rt3, rt2, rt1, rt0);                       \ +                                                                     \ +    rt3 = mipsdsp_##func(rt3, sa, env);                              \ +    rt2 = mipsdsp_##func(rt2, sa, env);                              \ +    rt1 = mipsdsp_##func(rt1, sa, env);                              \ +    rt0 = mipsdsp_##func(rt0, sa, env);                              \ +                                                                     \ +    return MIPSDSP_RETURN32_8(rt3, rt2, rt1, rt0);                   \ +} + +SHIFT_QB_ENV(shll, lshift8); +SHIFT_QB(shrl, rshift_u8); + +SHIFT_QB(shra, rashift8); +SHIFT_QB(shra_r, rnd8_rashift); + +#undef SHIFT_QB +#undef SHIFT_QB_ENV + +#if defined(TARGET_MIPS64) +#define SHIFT_OB(name, func) \ +target_ulong helper_##name##_ob(target_ulong rt, target_ulong sa) \ +{                                                                        \ +    int i;                                                               \ +    uint8_t rt_t[8];                                                     \ +    uint64_t temp;                                                       \ +                                                                         \ +    sa = sa & 0x07;                                                      \ +    temp = 0;                                                            \ +                                                                         \ +    for (i = 0; i < 8; i++) {                                            \ +        rt_t[i] = (rt >> (8 * i)) & MIPSDSP_Q0;                          \ +        rt_t[i] = mipsdsp_##func(rt_t[i], sa);                           \ +        temp |= (uint64_t)rt_t[i] << (8 * i);                            \ +    }                                                                    \ +                                                                         \ +    return temp;                                                         \ +} + +#define SHIFT_OB_ENV(name, func) \ +target_ulong helper_##name##_ob(target_ulong rt, target_ulong sa, \ +                                CPUMIPSState *env)                       \ +{                                                                        \ +    int i;                                                               \ +    uint8_t rt_t[8];                                                     \ +    uint64_t temp;                                                       \ +                                                                         \ +    sa = sa & 0x07;                                                      \ +    temp = 0;                                                            \ +                                                                         \ +    for (i = 0; i < 8; i++) {                                            \ +        rt_t[i] = (rt >> (8 * i)) & MIPSDSP_Q0;                          \ +        rt_t[i] = mipsdsp_##func(rt_t[i], sa, env);                      \ +        temp |= (uint64_t)rt_t[i] << (8 * i);                            \ +    }                                                                    \ +                                                                         \ +    return temp;                                                         \ +} + +SHIFT_OB_ENV(shll, lshift8); +SHIFT_OB(shrl, rshift_u8); + +SHIFT_OB(shra, rashift8); +SHIFT_OB(shra_r, rnd8_rashift); + +#undef SHIFT_OB +#undef SHIFT_OB_ENV + +#endif + +#define SHIFT_PH(name, func) \ +target_ulong helper_##name##_ph(target_ulong sa, target_ulong rt, \ +                                CPUMIPSState *env)                \ +{                                                                 \ +    uint16_t rth, rtl;                                            \ +                                                                  \ +    sa = sa & 0x0F;                                               \ +                                                                  \ +    MIPSDSP_SPLIT32_16(rt, rth, rtl);                             \ +                                                                  \ +    rth = mipsdsp_##func(rth, sa, env);                           \ +    rtl = mipsdsp_##func(rtl, sa, env);                           \ +                                                                  \ +    return MIPSDSP_RETURN32_16(rth, rtl);                         \ +} + +SHIFT_PH(shll, lshift16); +SHIFT_PH(shll_s, sat16_lshift); + +#undef SHIFT_PH + +#if defined(TARGET_MIPS64) +#define SHIFT_QH(name, func) \ +target_ulong helper_##name##_qh(target_ulong rt, target_ulong sa) \ +{                                                                 \ +    uint16_t rt3, rt2, rt1, rt0;                                  \ +                                                                  \ +    sa = sa & 0x0F;                                               \ +                                                                  \ +    MIPSDSP_SPLIT64_16(rt, rt3, rt2, rt1, rt0);                   \ +                                                                  \ +    rt3 = mipsdsp_##func(rt3, sa);                                \ +    rt2 = mipsdsp_##func(rt2, sa);                                \ +    rt1 = mipsdsp_##func(rt1, sa);                                \ +    rt0 = mipsdsp_##func(rt0, sa);                                \ +                                                                  \ +    return MIPSDSP_RETURN64_16(rt3, rt2, rt1, rt0);               \ +} + +#define SHIFT_QH_ENV(name, func) \ +target_ulong helper_##name##_qh(target_ulong rt, target_ulong sa, \ +                                CPUMIPSState *env)                \ +{                                                                 \ +    uint16_t rt3, rt2, rt1, rt0;                                  \ +                                                                  \ +    sa = sa & 0x0F;                                               \ +                                                                  \ +    MIPSDSP_SPLIT64_16(rt, rt3, rt2, rt1, rt0);                   \ +                                                                  \ +    rt3 = mipsdsp_##func(rt3, sa, env);                           \ +    rt2 = mipsdsp_##func(rt2, sa, env);                           \ +    rt1 = mipsdsp_##func(rt1, sa, env);                           \ +    rt0 = mipsdsp_##func(rt0, sa, env);                           \ +                                                                  \ +    return MIPSDSP_RETURN64_16(rt3, rt2, rt1, rt0);               \ +} + +SHIFT_QH_ENV(shll, lshift16); +SHIFT_QH_ENV(shll_s, sat16_lshift); + +SHIFT_QH(shrl, rshift_u16); +SHIFT_QH(shra, rashift16); +SHIFT_QH(shra_r, rnd16_rashift); + +#undef SHIFT_QH +#undef SHIFT_QH_ENV + +#endif + +#define SHIFT_W(name, func) \ +target_ulong helper_##name##_w(target_ulong sa, target_ulong rt) \ +{                                                                       \ +    uint32_t temp;                                                      \ +                                                                        \ +    sa = sa & 0x1F;                                                     \ +    temp = mipsdsp_##func(rt, sa);                                      \ +                                                                        \ +    return (target_long)(int32_t)temp;                                  \ +} + +#define SHIFT_W_ENV(name, func) \ +target_ulong helper_##name##_w(target_ulong sa, target_ulong rt, \ +                               CPUMIPSState *env) \ +{                                                                       \ +    uint32_t temp;                                                      \ +                                                                        \ +    sa = sa & 0x1F;                                                     \ +    temp = mipsdsp_##func(rt, sa, env);                                 \ +                                                                        \ +    return (target_long)(int32_t)temp;                                  \ +} + +SHIFT_W_ENV(shll_s, sat32_lshift); +SHIFT_W(shra_r, rnd32_rashift); + +#undef SHIFT_W +#undef SHIFT_W_ENV + +#if defined(TARGET_MIPS64) +#define SHIFT_PW(name, func) \ +target_ulong helper_##name##_pw(target_ulong rt, target_ulong sa) \ +{                                                                 \ +    uint32_t rt1, rt0;                                            \ +                                                                  \ +    sa = sa & 0x1F;                                               \ +    MIPSDSP_SPLIT64_32(rt, rt1, rt0);                             \ +                                                                  \ +    rt1 = mipsdsp_##func(rt1, sa);                                \ +    rt0 = mipsdsp_##func(rt0, sa);                                \ +                                                                  \ +    return MIPSDSP_RETURN64_32(rt1, rt0);                         \ +} + +#define SHIFT_PW_ENV(name, func) \ +target_ulong helper_##name##_pw(target_ulong rt, target_ulong sa, \ +                                CPUMIPSState *env)                \ +{                                                                 \ +    uint32_t rt1, rt0;                                            \ +                                                                  \ +    sa = sa & 0x1F;                                               \ +    MIPSDSP_SPLIT64_32(rt, rt1, rt0);                             \ +                                                                  \ +    rt1 = mipsdsp_##func(rt1, sa, env);                           \ +    rt0 = mipsdsp_##func(rt0, sa, env);                           \ +                                                                  \ +    return MIPSDSP_RETURN64_32(rt1, rt0);                         \ +} + +SHIFT_PW_ENV(shll, lshift32); +SHIFT_PW_ENV(shll_s, sat32_lshift); + +SHIFT_PW(shra, rashift32); +SHIFT_PW(shra_r, rnd32_rashift); + +#undef SHIFT_PW +#undef SHIFT_PW_ENV + +#endif + +#define SHIFT_PH(name, func) \ +target_ulong helper_##name##_ph(target_ulong sa, target_ulong rt) \ +{                                                                    \ +    uint16_t rth, rtl;                                               \ +                                                                     \ +    sa = sa & 0x0F;                                                  \ +                                                                     \ +    MIPSDSP_SPLIT32_16(rt, rth, rtl);                                \ +                                                                     \ +    rth = mipsdsp_##func(rth, sa);                                   \ +    rtl = mipsdsp_##func(rtl, sa);                                   \ +                                                                     \ +    return MIPSDSP_RETURN32_16(rth, rtl);                            \ +} + +SHIFT_PH(shrl, rshift_u16); +SHIFT_PH(shra, rashift16); +SHIFT_PH(shra_r, rnd16_rashift); + +#undef SHIFT_PH + +/** DSP Multiply Sub-class insns **/ +/* Return value made up by two 16bits value. + * FIXME give the macro a better name. + */ +#define MUL_RETURN32_16_PH(name, func, \ +                           rsmov1, rsmov2, rsfilter, \ +                           rtmov1, rtmov2, rtfilter) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt, \ +                           CPUMIPSState *env)                \ +{                                                            \ +    uint16_t rsB, rsA, rtB, rtA;                             \ +                                                             \ +    rsB = (rs >> rsmov1) & rsfilter;                         \ +    rsA = (rs >> rsmov2) & rsfilter;                         \ +    rtB = (rt >> rtmov1) & rtfilter;                         \ +    rtA = (rt >> rtmov2) & rtfilter;                         \ +                                                             \ +    rsB = mipsdsp_##func(rsB, rtB, env);                     \ +    rsA = mipsdsp_##func(rsA, rtA, env);                     \ +                                                             \ +    return MIPSDSP_RETURN32_16(rsB, rsA);                    \ +} + +MUL_RETURN32_16_PH(muleu_s_ph_qbl, mul_u8_u16, \ +                      24, 16, MIPSDSP_Q0, \ +                      16, 0, MIPSDSP_LO); +MUL_RETURN32_16_PH(muleu_s_ph_qbr, mul_u8_u16, \ +                      8, 0, MIPSDSP_Q0, \ +                      16, 0, MIPSDSP_LO); +MUL_RETURN32_16_PH(mulq_rs_ph, rndq15_mul_q15_q15, \ +                      16, 0, MIPSDSP_LO, \ +                      16, 0, MIPSDSP_LO); +MUL_RETURN32_16_PH(mul_ph, mul_i16_i16, \ +                      16, 0, MIPSDSP_LO, \ +                      16, 0, MIPSDSP_LO); +MUL_RETURN32_16_PH(mul_s_ph, sat16_mul_i16_i16, \ +                      16, 0, MIPSDSP_LO, \ +                      16, 0, MIPSDSP_LO); +MUL_RETURN32_16_PH(mulq_s_ph, sat16_mul_q15_q15, \ +                      16, 0, MIPSDSP_LO, \ +                      16, 0, MIPSDSP_LO); + +#undef MUL_RETURN32_16_PH + +#define MUL_RETURN32_32_ph(name, func, movbits) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt, \ +                                  CPUMIPSState *env)         \ +{                                                            \ +    int16_t rsh, rth;                                        \ +    int32_t temp;                                            \ +                                                             \ +    rsh = (rs >> movbits) & MIPSDSP_LO;                      \ +    rth = (rt >> movbits) & MIPSDSP_LO;                      \ +    temp = mipsdsp_##func(rsh, rth, env);                    \ +                                                             \ +    return (target_long)(int32_t)temp;                       \ +} + +MUL_RETURN32_32_ph(muleq_s_w_phl, mul_q15_q15_overflowflag21, 16); +MUL_RETURN32_32_ph(muleq_s_w_phr, mul_q15_q15_overflowflag21, 0); + +#undef MUL_RETURN32_32_ph + +#define MUL_VOID_PH(name, use_ac_env) \ +void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt,        \ +                          CPUMIPSState *env)                             \ +{                                                                        \ +    int16_t rsh, rsl, rth, rtl;                                          \ +    int32_t tempB, tempA;                                                \ +    int64_t acc, dotp;                                                   \ +                                                                         \ +    MIPSDSP_SPLIT32_16(rs, rsh, rsl);                                    \ +    MIPSDSP_SPLIT32_16(rt, rth, rtl);                                    \ +                                                                         \ +    if (use_ac_env == 1) {                                               \ +        tempB = mipsdsp_mul_q15_q15(ac, rsh, rth, env);                  \ +        tempA = mipsdsp_mul_q15_q15(ac, rsl, rtl, env);                  \ +    } else {                                                             \ +        tempB = mipsdsp_mul_u16_u16(rsh, rth);                           \ +        tempA = mipsdsp_mul_u16_u16(rsl, rtl);                           \ +    }                                                                    \ +                                                                         \ +    dotp = (int64_t)tempB - (int64_t)tempA;                              \ +    acc = ((uint64_t)env->active_tc.HI[ac] << 32) |                      \ +          ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO);               \ +    dotp = dotp + acc;                                                   \ +    env->active_tc.HI[ac] = (target_long)(int32_t)                       \ +                            ((dotp & MIPSDSP_LHI) >> 32);                \ +    env->active_tc.LO[ac] = (target_long)(int32_t)(dotp & MIPSDSP_LLO);  \ +} + +MUL_VOID_PH(mulsaq_s_w_ph, 1); +MUL_VOID_PH(mulsa_w_ph, 0); + +#undef MUL_VOID_PH + +#if defined(TARGET_MIPS64) +#define MUL_RETURN64_16_QH(name, func, \ +                           rsmov1, rsmov2, rsmov3, rsmov4, rsfilter, \ +                           rtmov1, rtmov2, rtmov3, rtmov4, rtfilter) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt,         \ +                           CPUMIPSState *env)                        \ +{                                                                    \ +    uint16_t rs3, rs2, rs1, rs0;                                     \ +    uint16_t rt3, rt2, rt1, rt0;                                     \ +    uint16_t tempD, tempC, tempB, tempA;                             \ +                                                                     \ +    rs3 = (rs >> rsmov1) & rsfilter;                                 \ +    rs2 = (rs >> rsmov2) & rsfilter;                                 \ +    rs1 = (rs >> rsmov3) & rsfilter;                                 \ +    rs0 = (rs >> rsmov4) & rsfilter;                                 \ +    rt3 = (rt >> rtmov1) & rtfilter;                                 \ +    rt2 = (rt >> rtmov2) & rtfilter;                                 \ +    rt1 = (rt >> rtmov3) & rtfilter;                                 \ +    rt0 = (rt >> rtmov4) & rtfilter;                                 \ +                                                                     \ +    tempD = mipsdsp_##func(rs3, rt3, env);                           \ +    tempC = mipsdsp_##func(rs2, rt2, env);                           \ +    tempB = mipsdsp_##func(rs1, rt1, env);                           \ +    tempA = mipsdsp_##func(rs0, rt0, env);                           \ +                                                                     \ +    return MIPSDSP_RETURN64_16(tempD, tempC, tempB, tempA);          \ +} + +MUL_RETURN64_16_QH(muleu_s_qh_obl, mul_u8_u16, \ +                   56, 48, 40, 32, MIPSDSP_Q0, \ +                   48, 32, 16, 0, MIPSDSP_LO); +MUL_RETURN64_16_QH(muleu_s_qh_obr, mul_u8_u16, \ +                   24, 16, 8, 0, MIPSDSP_Q0, \ +                   48, 32, 16, 0, MIPSDSP_LO); +MUL_RETURN64_16_QH(mulq_rs_qh, rndq15_mul_q15_q15, \ +                   48, 32, 16, 0, MIPSDSP_LO, \ +                   48, 32, 16, 0, MIPSDSP_LO); + +#undef MUL_RETURN64_16_QH + +#define MUL_RETURN64_32_QH(name, \ +                           rsmov1, rsmov2, \ +                           rtmov1, rtmov2) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt, \ +                           CPUMIPSState *env)                \ +{                                                            \ +    uint16_t rsB, rsA;                                       \ +    uint16_t rtB, rtA;                                       \ +    uint32_t tempB, tempA;                                   \ +                                                             \ +    rsB = (rs >> rsmov1) & MIPSDSP_LO;                       \ +    rsA = (rs >> rsmov2) & MIPSDSP_LO;                       \ +    rtB = (rt >> rtmov1) & MIPSDSP_LO;                       \ +    rtA = (rt >> rtmov2) & MIPSDSP_LO;                       \ +                                                             \ +    tempB = mipsdsp_mul_q15_q15(5, rsB, rtB, env);           \ +    tempA = mipsdsp_mul_q15_q15(5, rsA, rtA, env);           \ +                                                             \ +    return ((uint64_t)tempB << 32) | (uint64_t)tempA;        \ +} + +MUL_RETURN64_32_QH(muleq_s_pw_qhl, 48, 32, 48, 32); +MUL_RETURN64_32_QH(muleq_s_pw_qhr, 16, 0, 16, 0); + +#undef MUL_RETURN64_32_QH + +void helper_mulsaq_s_w_qh(target_ulong rs, target_ulong rt, uint32_t ac, +                          CPUMIPSState *env) +{ +    int16_t rs3, rs2, rs1, rs0; +    int16_t rt3, rt2, rt1, rt0; +    int32_t tempD, tempC, tempB, tempA; +    int64_t acc[2]; +    int64_t temp[2]; +    int64_t temp_sum; + +    MIPSDSP_SPLIT64_16(rs, rs3, rs2, rs1, rs0); +    MIPSDSP_SPLIT64_16(rt, rt3, rt2, rt1, rt0); + +    tempD = mipsdsp_mul_q15_q15(ac, rs3, rt3, env); +    tempC = mipsdsp_mul_q15_q15(ac, rs2, rt2, env); +    tempB = mipsdsp_mul_q15_q15(ac, rs1, rt1, env); +    tempA = mipsdsp_mul_q15_q15(ac, rs0, rt0, env); + +    temp[0] = ((int32_t)tempD - (int32_t)tempC) + +              ((int32_t)tempB - (int32_t)tempA); +    temp[0] = (int64_t)(temp[0] << 30) >> 30; +    if (((temp[0] >> 33) & 0x01) == 0) { +        temp[1] = 0x00; +    } else { +        temp[1] = ~0ull; +    } + +    acc[0] = env->active_tc.LO[ac]; +    acc[1] = env->active_tc.HI[ac]; + +    temp_sum = acc[0] + temp[0]; +    if (((uint64_t)temp_sum < (uint64_t)acc[0]) && +       ((uint64_t)temp_sum < (uint64_t)temp[0])) { +        acc[1] += 1; +    } +    acc[0] = temp_sum; +    acc[1] += temp[1]; + +    env->active_tc.HI[ac] = acc[1]; +    env->active_tc.LO[ac] = acc[0]; +} +#endif + +#define DP_QB(name, func, is_add, rsmov1, rsmov2, rtmov1, rtmov2) \ +void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt,        \ +                   CPUMIPSState *env)                                    \ +{                                                                        \ +    uint8_t rs3, rs2;                                                    \ +    uint8_t rt3, rt2;                                                    \ +    uint16_t tempB, tempA;                                               \ +    uint64_t tempC, dotp;                                                \ +                                                                         \ +    rs3 = (rs >> rsmov1) & MIPSDSP_Q0;                                   \ +    rs2 = (rs >> rsmov2) & MIPSDSP_Q0;                                   \ +    rt3 = (rt >> rtmov1) & MIPSDSP_Q0;                                   \ +    rt2 = (rt >> rtmov2) & MIPSDSP_Q0;                                   \ +    tempB = mipsdsp_##func(rs3, rt3);                                    \ +    tempA = mipsdsp_##func(rs2, rt2);                                    \ +    dotp = (int64_t)tempB + (int64_t)tempA;                              \ +    if (is_add) {                                                        \ +        tempC = (((uint64_t)env->active_tc.HI[ac] << 32) |               \ +                 ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO))        \ +            + dotp;                                                      \ +    } else {                                                             \ +        tempC = (((uint64_t)env->active_tc.HI[ac] << 32) |               \ +                 ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO))        \ +            - dotp;                                                      \ +    }                                                                    \ +                                                                         \ +    env->active_tc.HI[ac] = (target_long)(int32_t)                       \ +                            ((tempC & MIPSDSP_LHI) >> 32);               \ +    env->active_tc.LO[ac] = (target_long)(int32_t)(tempC & MIPSDSP_LLO); \ +} + +DP_QB(dpau_h_qbl, mul_u8_u8, 1, 24, 16, 24, 16); +DP_QB(dpau_h_qbr, mul_u8_u8, 1, 8, 0, 8, 0); +DP_QB(dpsu_h_qbl, mul_u8_u8, 0, 24, 16, 24, 16); +DP_QB(dpsu_h_qbr, mul_u8_u8, 0, 8, 0, 8, 0); + +#undef DP_QB + +#if defined(TARGET_MIPS64) +#define DP_OB(name, add_sub, \ +              rsmov1, rsmov2, rsmov3, rsmov4, \ +              rtmov1, rtmov2, rtmov3, rtmov4) \ +void helper_##name(target_ulong rs, target_ulong rt, uint32_t ac,       \ +                       CPUMIPSState *env)                               \ +{                                                                       \ +    uint8_t rsD, rsC, rsB, rsA;                                         \ +    uint8_t rtD, rtC, rtB, rtA;                                         \ +    uint16_t tempD, tempC, tempB, tempA;                                \ +    uint64_t temp[2];                                                   \ +    uint64_t acc[2];                                                    \ +    uint64_t temp_sum;                                                  \ +                                                                        \ +    temp[0] = 0;                                                        \ +    temp[1] = 0;                                                        \ +                                                                        \ +    rsD = (rs >> rsmov1) & MIPSDSP_Q0;                                  \ +    rsC = (rs >> rsmov2) & MIPSDSP_Q0;                                  \ +    rsB = (rs >> rsmov3) & MIPSDSP_Q0;                                  \ +    rsA = (rs >> rsmov4) & MIPSDSP_Q0;                                  \ +    rtD = (rt >> rtmov1) & MIPSDSP_Q0;                                  \ +    rtC = (rt >> rtmov2) & MIPSDSP_Q0;                                  \ +    rtB = (rt >> rtmov3) & MIPSDSP_Q0;                                  \ +    rtA = (rt >> rtmov4) & MIPSDSP_Q0;                                  \ +                                                                        \ +    tempD = mipsdsp_mul_u8_u8(rsD, rtD);                                \ +    tempC = mipsdsp_mul_u8_u8(rsC, rtC);                                \ +    tempB = mipsdsp_mul_u8_u8(rsB, rtB);                                \ +    tempA = mipsdsp_mul_u8_u8(rsA, rtA);                                \ +                                                                        \ +    temp[0] = (uint64_t)tempD + (uint64_t)tempC +                       \ +      (uint64_t)tempB + (uint64_t)tempA;                                \ +                                                                        \ +    acc[0] = env->active_tc.LO[ac];                                     \ +    acc[1] = env->active_tc.HI[ac];                                     \ +                                                                        \ +    if (add_sub) {                                                      \ +        temp_sum = acc[0] + temp[0];                                    \ +        if (((uint64_t)temp_sum < (uint64_t)acc[0]) &&                  \ +            ((uint64_t)temp_sum < (uint64_t)temp[0])) {                 \ +            acc[1] += 1;                                                \ +        }                                                               \ +        temp[0] = temp_sum;                                             \ +        temp[1] = acc[1] + temp[1];                                     \ +    } else {                                                            \ +        temp_sum = acc[0] - temp[0];                                    \ +        if ((uint64_t)temp_sum > (uint64_t)acc[0]) {                    \ +            acc[1] -= 1;                                                \ +        }                                                               \ +        temp[0] = temp_sum;                                             \ +        temp[1] = acc[1] - temp[1];                                     \ +    }                                                                   \ +                                                                        \ +    env->active_tc.HI[ac] = temp[1];                                    \ +    env->active_tc.LO[ac] = temp[0];                                    \ +} + +DP_OB(dpau_h_obl, 1, 56, 48, 40, 32, 56, 48, 40, 32); +DP_OB(dpau_h_obr, 1, 24, 16, 8, 0, 24, 16, 8, 0); +DP_OB(dpsu_h_obl, 0, 56, 48, 40, 32, 56, 48, 40, 32); +DP_OB(dpsu_h_obr, 0, 24, 16, 8, 0, 24, 16, 8, 0); + +#undef DP_OB +#endif + +#define DP_NOFUNC_PH(name, is_add, rsmov1, rsmov2, rtmov1, rtmov2)             \ +void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt,              \ +                   CPUMIPSState *env)                                          \ +{                                                                              \ +    int16_t rsB, rsA, rtB, rtA;                                                \ +    int32_t  tempA, tempB;                                                     \ +    int64_t  acc;                                                              \ +                                                                               \ +    rsB = (rs >> rsmov1) & MIPSDSP_LO;                                         \ +    rsA = (rs >> rsmov2) & MIPSDSP_LO;                                         \ +    rtB = (rt >> rtmov1) & MIPSDSP_LO;                                         \ +    rtA = (rt >> rtmov2) & MIPSDSP_LO;                                         \ +                                                                               \ +    tempB = (int32_t)rsB * (int32_t)rtB;                                       \ +    tempA = (int32_t)rsA * (int32_t)rtA;                                       \ +                                                                               \ +    acc = ((uint64_t)env->active_tc.HI[ac] << 32) |                            \ +          ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO);                     \ +                                                                               \ +    if (is_add) {                                                              \ +        acc = acc + ((int64_t)tempB + (int64_t)tempA);                         \ +    } else {                                                                   \ +        acc = acc - ((int64_t)tempB + (int64_t)tempA);                         \ +    }                                                                          \ +                                                                               \ +    env->active_tc.HI[ac] = (target_long)(int32_t)((acc & MIPSDSP_LHI) >> 32); \ +    env->active_tc.LO[ac] = (target_long)(int32_t)(acc & MIPSDSP_LLO);         \ +} + +DP_NOFUNC_PH(dpa_w_ph, 1, 16, 0, 16, 0); +DP_NOFUNC_PH(dpax_w_ph, 1, 16, 0, 0, 16); +DP_NOFUNC_PH(dps_w_ph, 0, 16, 0, 16, 0); +DP_NOFUNC_PH(dpsx_w_ph, 0, 16, 0, 0, 16); +#undef DP_NOFUNC_PH + +#define DP_HASFUNC_PH(name, is_add, rsmov1, rsmov2, rtmov1, rtmov2) \ +void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt,   \ +                   CPUMIPSState *env)                      \ +{                                                          \ +    int16_t rsB, rsA, rtB, rtA;                            \ +    int32_t tempB, tempA;                                  \ +    int64_t acc, dotp;                                     \ +                                                           \ +    rsB = (rs >> rsmov1) & MIPSDSP_LO;                     \ +    rsA = (rs >> rsmov2) & MIPSDSP_LO;                     \ +    rtB = (rt >> rtmov1) & MIPSDSP_LO;                     \ +    rtA = (rt >> rtmov2) & MIPSDSP_LO;                     \ +                                                           \ +    tempB = mipsdsp_mul_q15_q15(ac, rsB, rtB, env);        \ +    tempA = mipsdsp_mul_q15_q15(ac, rsA, rtA, env);        \ +                                                           \ +    dotp = (int64_t)tempB + (int64_t)tempA;                \ +    acc = ((uint64_t)env->active_tc.HI[ac] << 32) |        \ +          ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO); \ +                                                           \ +    if (is_add) {                                          \ +        acc = acc + dotp;                                  \ +    } else {                                               \ +        acc = acc - dotp;                                  \ +    }                                                      \ +                                                           \ +    env->active_tc.HI[ac] = (target_long)(int32_t)         \ +        ((acc & MIPSDSP_LHI) >> 32);                       \ +    env->active_tc.LO[ac] = (target_long)(int32_t)         \ +        (acc & MIPSDSP_LLO);                               \ +} + +DP_HASFUNC_PH(dpaq_s_w_ph, 1, 16, 0, 16, 0); +DP_HASFUNC_PH(dpaqx_s_w_ph, 1, 16, 0, 0, 16); +DP_HASFUNC_PH(dpsq_s_w_ph, 0, 16, 0, 16, 0); +DP_HASFUNC_PH(dpsqx_s_w_ph, 0, 16, 0, 0, 16); + +#undef DP_HASFUNC_PH + +#define DP_128OPERATION_PH(name, is_add) \ +void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt, \ +                          CPUMIPSState *env)                             \ +{                                                                        \ +    int16_t rsh, rsl, rth, rtl;                                          \ +    int32_t tempB, tempA, tempC62_31, tempC63;                           \ +    int64_t acc, dotp, tempC;                                            \ +                                                                         \ +    MIPSDSP_SPLIT32_16(rs, rsh, rsl);                                    \ +    MIPSDSP_SPLIT32_16(rt, rth, rtl);                                    \ +                                                                         \ +    tempB = mipsdsp_mul_q15_q15(ac, rsh, rtl, env);                      \ +    tempA = mipsdsp_mul_q15_q15(ac, rsl, rth, env);                      \ +                                                                         \ +    dotp = (int64_t)tempB + (int64_t)tempA;                              \ +    acc = ((uint64_t)env->active_tc.HI[ac] << 32) |                      \ +          ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO);               \ +    if (is_add) {                                                        \ +        tempC = acc + dotp;                                              \ +    } else {                                                             \ +        tempC = acc - dotp;                                              \ +    }                                                                    \ +    tempC63 = (tempC >> 63) & 0x01;                                      \ +    tempC62_31 = (tempC >> 31) & 0xFFFFFFFF;                             \ +                                                                         \ +    if ((tempC63 == 0) && (tempC62_31 != 0x00000000)) {                  \ +        tempC = 0x7FFFFFFF;                                              \ +        set_DSPControl_overflow_flag(1, 16 + ac, env);                   \ +    }                                                                    \ +                                                                         \ +    if ((tempC63 == 1) && (tempC62_31 != 0xFFFFFFFF)) {                  \ +        tempC = (int64_t)(int32_t)0x80000000;                            \ +        set_DSPControl_overflow_flag(1, 16 + ac, env);                   \ +    }                                                                    \ +                                                                         \ +    env->active_tc.HI[ac] = (target_long)(int32_t)                       \ +        ((tempC & MIPSDSP_LHI) >> 32);                                   \ +    env->active_tc.LO[ac] = (target_long)(int32_t)                       \ +        (tempC & MIPSDSP_LLO);                                           \ +} + +DP_128OPERATION_PH(dpaqx_sa_w_ph, 1); +DP_128OPERATION_PH(dpsqx_sa_w_ph, 0); + +#undef DP_128OPERATION_HP + +#if defined(TARGET_MIPS64) +#define DP_QH(name, is_add, use_ac_env) \ +void helper_##name(target_ulong rs, target_ulong rt, uint32_t ac,    \ +                   CPUMIPSState *env)                                \ +{                                                                    \ +    int32_t rs3, rs2, rs1, rs0;                                      \ +    int32_t rt3, rt2, rt1, rt0;                                      \ +    int32_t tempD, tempC, tempB, tempA;                              \ +    int64_t acc[2];                                                  \ +    int64_t temp[2];                                                 \ +    int64_t temp_sum;                                                \ +                                                                     \ +    MIPSDSP_SPLIT64_16(rs, rs3, rs2, rs1, rs0);                      \ +    MIPSDSP_SPLIT64_16(rt, rt3, rt2, rt1, rt0);                      \ +                                                                     \ +    if (use_ac_env) {                                                \ +        tempD = mipsdsp_mul_q15_q15(ac, rs3, rt3, env);              \ +        tempC = mipsdsp_mul_q15_q15(ac, rs2, rt2, env);              \ +        tempB = mipsdsp_mul_q15_q15(ac, rs1, rt1, env);              \ +        tempA = mipsdsp_mul_q15_q15(ac, rs0, rt0, env);              \ +    } else {                                                         \ +        tempD = mipsdsp_mul_u16_u16(rs3, rt3);                       \ +        tempC = mipsdsp_mul_u16_u16(rs2, rt2);                       \ +        tempB = mipsdsp_mul_u16_u16(rs1, rt1);                       \ +        tempA = mipsdsp_mul_u16_u16(rs0, rt0);                       \ +    }                                                                \ +                                                                     \ +    temp[0] = (int64_t)tempD + (int64_t)tempC +                      \ +              (int64_t)tempB + (int64_t)tempA;                       \ +                                                                     \ +    if (temp[0] >= 0) {                                              \ +        temp[1] = 0;                                                 \ +    } else {                                                         \ +        temp[1] = ~0ull;                                             \ +    }                                                                \ +                                                                     \ +    acc[1] = env->active_tc.HI[ac];                                  \ +    acc[0] = env->active_tc.LO[ac];                                  \ +                                                                     \ +    if (is_add) {                                                    \ +        temp_sum = acc[0] + temp[0];                                 \ +        if (((uint64_t)temp_sum < (uint64_t)acc[0]) &&               \ +            ((uint64_t)temp_sum < (uint64_t)temp[0])) {              \ +            acc[1] = acc[1] + 1;                                     \ +        }                                                            \ +        temp[0] = temp_sum;                                          \ +        temp[1] = acc[1] + temp[1];                                  \ +    } else {                                                         \ +        temp_sum = acc[0] - temp[0];                                 \ +        if ((uint64_t)temp_sum > (uint64_t)acc[0]) {                 \ +            acc[1] = acc[1] - 1;                                     \ +        }                                                            \ +        temp[0] = temp_sum;                                          \ +        temp[1] = acc[1] - temp[1];                                  \ +    }                                                                \ +                                                                     \ +    env->active_tc.HI[ac] = temp[1];                                 \ +    env->active_tc.LO[ac] = temp[0];                                 \ +} + +DP_QH(dpa_w_qh, 1, 0); +DP_QH(dpaq_s_w_qh, 1, 1); +DP_QH(dps_w_qh, 0, 0); +DP_QH(dpsq_s_w_qh, 0, 1); + +#undef DP_QH + +#endif + +#define DP_L_W(name, is_add) \ +void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt,      \ +                   CPUMIPSState *env)                                  \ +{                                                                      \ +    int32_t temp63;                                                    \ +    int64_t dotp, acc;                                                 \ +    uint64_t temp;                                                     \ +    bool overflow;                                                     \ +                                                                       \ +    dotp = mipsdsp_mul_q31_q31(ac, rs, rt, env);                       \ +    acc = ((uint64_t)env->active_tc.HI[ac] << 32) |                    \ +          ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO);             \ +    if (is_add) {                                                      \ +        temp = acc + dotp;                                             \ +        overflow = MIPSDSP_OVERFLOW_ADD((uint64_t)acc, (uint64_t)dotp, \ +                                        temp, (0x01ull << 63));        \ +    } else {                                                           \ +        temp = acc - dotp;                                             \ +        overflow = MIPSDSP_OVERFLOW_SUB((uint64_t)acc, (uint64_t)dotp, \ +                                        temp, (0x01ull << 63));        \ +    }                                                                  \ +                                                                       \ +    if (overflow) {                                                    \ +        temp63 = (temp >> 63) & 0x01;                                  \ +        if (temp63 == 1) {                                             \ +            temp = (0x01ull << 63) - 1;                                \ +        } else {                                                       \ +            temp = 0x01ull << 63;                                      \ +        }                                                              \ +                                                                       \ +        set_DSPControl_overflow_flag(1, 16 + ac, env);                 \ +    }                                                                  \ +                                                                       \ +    env->active_tc.HI[ac] = (target_long)(int32_t)                     \ +        ((temp & MIPSDSP_LHI) >> 32);                                  \ +    env->active_tc.LO[ac] = (target_long)(int32_t)                     \ +        (temp & MIPSDSP_LLO);                                          \ +} + +DP_L_W(dpaq_sa_l_w, 1); +DP_L_W(dpsq_sa_l_w, 0); + +#undef DP_L_W + +#if defined(TARGET_MIPS64) +#define DP_L_PW(name, func) \ +void helper_##name(target_ulong rs, target_ulong rt, uint32_t ac, \ +                   CPUMIPSState *env)                             \ +{                                                                 \ +    int32_t rs1, rs0;                                             \ +    int32_t rt1, rt0;                                             \ +    int64_t tempB[2], tempA[2];                                   \ +    int64_t temp[2];                                              \ +    int64_t acc[2];                                               \ +    int64_t temp_sum;                                             \ +                                                                  \ +    temp[0] = 0;                                                  \ +    temp[1] = 0;                                                  \ +                                                                  \ +    MIPSDSP_SPLIT64_32(rs, rs1, rs0);                             \ +    MIPSDSP_SPLIT64_32(rt, rt1, rt0);                             \ +                                                                  \ +    tempB[0] = mipsdsp_mul_q31_q31(ac, rs1, rt1, env);            \ +    tempA[0] = mipsdsp_mul_q31_q31(ac, rs0, rt0, env);            \ +                                                                  \ +    if (tempB[0] >= 0) {                                          \ +        tempB[1] = 0x00;                                          \ +    } else {                                                      \ +        tempB[1] = ~0ull;                                         \ +    }                                                             \ +                                                                  \ +    if (tempA[0] >= 0) {                                          \ +        tempA[1] = 0x00;                                          \ +    } else {                                                      \ +        tempA[1] = ~0ull;                                         \ +    }                                                             \ +                                                                  \ +    temp_sum = tempB[0] + tempA[0];                               \ +    if (((uint64_t)temp_sum < (uint64_t)tempB[0]) &&              \ +        ((uint64_t)temp_sum < (uint64_t)tempA[0])) {              \ +        temp[1] += 1;                                             \ +    }                                                             \ +    temp[0] = temp_sum;                                           \ +    temp[1] += tempB[1] + tempA[1];                               \ +                                                                  \ +    mipsdsp_##func(acc, ac, temp, env);                           \ +                                                                  \ +    env->active_tc.HI[ac] = acc[1];                               \ +    env->active_tc.LO[ac] = acc[0];                               \ +} + +DP_L_PW(dpaq_sa_l_pw, sat64_acc_add_q63); +DP_L_PW(dpsq_sa_l_pw, sat64_acc_sub_q63); + +#undef DP_L_PW + +void helper_mulsaq_s_l_pw(target_ulong rs, target_ulong rt, uint32_t ac, +                          CPUMIPSState *env) +{ +    int32_t rs1, rs0; +    int32_t rt1, rt0; +    int64_t tempB[2], tempA[2]; +    int64_t temp[2]; +    int64_t acc[2]; +    int64_t temp_sum; + +    rs1 = (rs >> 32) & MIPSDSP_LLO; +    rs0 = rs & MIPSDSP_LLO; +    rt1 = (rt >> 32) & MIPSDSP_LLO; +    rt0 = rt & MIPSDSP_LLO; + +    tempB[0] = mipsdsp_mul_q31_q31(ac, rs1, rt1, env); +    tempA[0] = mipsdsp_mul_q31_q31(ac, rs0, rt0, env); + +    if (tempB[0] >= 0) { +        tempB[1] = 0x00; +    } else { +        tempB[1] = ~0ull; +    } + +    if (tempA[0] >= 0) { +        tempA[1] = 0x00; +    } else { +        tempA[1] = ~0ull; +    } + +    acc[0] = env->active_tc.LO[ac]; +    acc[1] = env->active_tc.HI[ac]; + +    temp_sum = tempB[0] - tempA[0]; +    if ((uint64_t)temp_sum > (uint64_t)tempB[0]) { +        tempB[1] -= 1; +    } +    temp[0] = temp_sum; +    temp[1] = tempB[1] - tempA[1]; + +    if ((temp[1] & 0x01) == 0) { +        temp[1] = 0x00; +    } else { +        temp[1] = ~0ull; +    } + +    temp_sum = acc[0] + temp[0]; +    if (((uint64_t)temp_sum < (uint64_t)acc[0]) && +       ((uint64_t)temp_sum < (uint64_t)temp[0])) { +        acc[1] += 1; +    } +    acc[0] = temp_sum; +    acc[1] += temp[1]; + +    env->active_tc.HI[ac] = acc[1]; +    env->active_tc.LO[ac] = acc[0]; +} +#endif + +#define MAQ_S_W(name, mov) \ +void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt, \ +                   CPUMIPSState *env)                             \ +{                                                                 \ +    int16_t rsh, rth;                                             \ +    int32_t tempA;                                                \ +    int64_t tempL, acc;                                           \ +                                                                  \ +    rsh = (rs >> mov) & MIPSDSP_LO;                               \ +    rth = (rt >> mov) & MIPSDSP_LO;                               \ +    tempA  = mipsdsp_mul_q15_q15(ac, rsh, rth, env);              \ +    acc = ((uint64_t)env->active_tc.HI[ac] << 32) |               \ +          ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO);        \ +    tempL  = (int64_t)tempA + acc;                                \ +    env->active_tc.HI[ac] = (target_long)(int32_t)                \ +        ((tempL & MIPSDSP_LHI) >> 32);                            \ +    env->active_tc.LO[ac] = (target_long)(int32_t)                \ +        (tempL & MIPSDSP_LLO);                                    \ +} + +MAQ_S_W(maq_s_w_phl, 16); +MAQ_S_W(maq_s_w_phr, 0); + +#undef MAQ_S_W + +#define MAQ_SA_W(name, mov) \ +void helper_##name(uint32_t ac, target_ulong rs, target_ulong rt,        \ +                   CPUMIPSState *env)                                    \ +{                                                                        \ +    int16_t rsh, rth;                                                    \ +    int32_t tempA;                                                       \ +                                                                         \ +    rsh = (rs >> mov) & MIPSDSP_LO;                                      \ +    rth = (rt >> mov) & MIPSDSP_LO;                                      \ +    tempA = mipsdsp_mul_q15_q15(ac, rsh, rth, env);                      \ +    tempA = mipsdsp_sat32_acc_q31(ac, tempA, env);                       \ +                                                                         \ +    env->active_tc.HI[ac] = (target_long)(int32_t)(((int64_t)tempA &     \ +                                                    MIPSDSP_LHI) >> 32); \ +    env->active_tc.LO[ac] = (target_long)(int32_t)((int64_t)tempA &      \ +                                                   MIPSDSP_LLO);         \ +} + +MAQ_SA_W(maq_sa_w_phl, 16); +MAQ_SA_W(maq_sa_w_phr, 0); + +#undef MAQ_SA_W + +#define MULQ_W(name, addvar) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt,   \ +                           CPUMIPSState *env)                  \ +{                                                              \ +    int32_t rs_t, rt_t;                                        \ +    int32_t tempI;                                             \ +    int64_t tempL;                                             \ +                                                               \ +    rs_t = rs & MIPSDSP_LLO;                                   \ +    rt_t = rt & MIPSDSP_LLO;                                   \ +                                                               \ +    if ((rs_t == 0x80000000) && (rt_t == 0x80000000)) {        \ +        tempL = 0x7FFFFFFF00000000ull;                         \ +        set_DSPControl_overflow_flag(1, 21, env);              \ +    } else {                                                   \ +        tempL  = ((int64_t)rs_t * (int64_t)rt_t) << 1;         \ +        tempL += addvar;                                       \ +    }                                                          \ +    tempI = (tempL & MIPSDSP_LHI) >> 32;                       \ +                                                               \ +    return (target_long)(int32_t)tempI;                        \ +} + +MULQ_W(mulq_s_w, 0); +MULQ_W(mulq_rs_w, 0x80000000ull); + +#undef MULQ_W + +#if defined(TARGET_MIPS64) + +#define MAQ_S_W_QH(name, mov) \ +void helper_##name(target_ulong rs, target_ulong rt, uint32_t ac, \ +                   CPUMIPSState *env)                             \ +{                                                                 \ +    int16_t rs_t, rt_t;                                           \ +    int32_t temp_mul;                                             \ +    int64_t temp[2];                                              \ +    int64_t acc[2];                                               \ +    int64_t temp_sum;                                             \ +                                                                  \ +    temp[0] = 0;                                                  \ +    temp[1] = 0;                                                  \ +                                                                  \ +    rs_t = (rs >> mov) & MIPSDSP_LO;                              \ +    rt_t = (rt >> mov) & MIPSDSP_LO;                              \ +    temp_mul = mipsdsp_mul_q15_q15(ac, rs_t, rt_t, env);          \ +                                                                  \ +    temp[0] = (int64_t)temp_mul;                                  \ +    if (temp[0] >= 0) {                                           \ +        temp[1] = 0x00;                                           \ +    } else {                                                      \ +        temp[1] = ~0ull;                                          \ +    }                                                             \ +                                                                  \ +    acc[0] = env->active_tc.LO[ac];                               \ +    acc[1] = env->active_tc.HI[ac];                               \ +                                                                  \ +    temp_sum = acc[0] + temp[0];                                  \ +    if (((uint64_t)temp_sum < (uint64_t)acc[0]) &&                \ +        ((uint64_t)temp_sum < (uint64_t)temp[0])) {               \ +        acc[1] += 1;                                              \ +    }                                                             \ +    acc[0] = temp_sum;                                            \ +    acc[1] += temp[1];                                            \ +                                                                  \ +    env->active_tc.HI[ac] = acc[1];                               \ +    env->active_tc.LO[ac] = acc[0];                               \ +} + +MAQ_S_W_QH(maq_s_w_qhll, 48); +MAQ_S_W_QH(maq_s_w_qhlr, 32); +MAQ_S_W_QH(maq_s_w_qhrl, 16); +MAQ_S_W_QH(maq_s_w_qhrr, 0); + +#undef MAQ_S_W_QH + +#define MAQ_SA_W(name, mov) \ +void helper_##name(target_ulong rs, target_ulong rt, uint32_t ac, \ +                   CPUMIPSState *env)                             \ +{                                                                 \ +    int16_t rs_t, rt_t;                                           \ +    int32_t temp;                                                 \ +    int64_t acc[2];                                               \ +                                                                  \ +    rs_t = (rs >> mov) & MIPSDSP_LO;                              \ +    rt_t = (rt >> mov) & MIPSDSP_LO;                              \ +    temp = mipsdsp_mul_q15_q15(ac, rs_t, rt_t, env);              \ +    temp = mipsdsp_sat32_acc_q31(ac, temp, env);                  \ +                                                                  \ +    acc[0] = (int64_t)(int32_t)temp;                              \ +    if (acc[0] >= 0) {                                            \ +        acc[1] = 0x00;                                            \ +    } else {                                                      \ +        acc[1] = ~0ull;                                           \ +    }                                                             \ +                                                                  \ +    env->active_tc.HI[ac] = acc[1];                               \ +    env->active_tc.LO[ac] = acc[0];                               \ +} + +MAQ_SA_W(maq_sa_w_qhll, 48); +MAQ_SA_W(maq_sa_w_qhlr, 32); +MAQ_SA_W(maq_sa_w_qhrl, 16); +MAQ_SA_W(maq_sa_w_qhrr, 0); + +#undef MAQ_SA_W + +#define MAQ_S_L_PW(name, mov) \ +void helper_##name(target_ulong rs, target_ulong rt, uint32_t ac, \ +                   CPUMIPSState *env)                             \ +{                                                                 \ +    int32_t rs_t, rt_t;                                           \ +    int64_t temp[2];                                              \ +    int64_t acc[2];                                               \ +    int64_t temp_sum;                                             \ +                                                                  \ +    temp[0] = 0;                                                  \ +    temp[1] = 0;                                                  \ +                                                                  \ +    rs_t = (rs >> mov) & MIPSDSP_LLO;                             \ +    rt_t = (rt >> mov) & MIPSDSP_LLO;                             \ +                                                                  \ +    temp[0] = mipsdsp_mul_q31_q31(ac, rs_t, rt_t, env);           \ +    if (temp[0] >= 0) {                                           \ +        temp[1] = 0x00;                                           \ +    } else {                                                      \ +        temp[1] = ~0ull;                                          \ +    }                                                             \ +                                                                  \ +    acc[0] = env->active_tc.LO[ac];                               \ +    acc[1] = env->active_tc.HI[ac];                               \ +                                                                  \ +    temp_sum = acc[0] + temp[0];                                  \ +    if (((uint64_t)temp_sum < (uint64_t)acc[0]) &&                \ +        ((uint64_t)temp_sum < (uint64_t)temp[0])) {               \ +        acc[1] += 1;                                              \ +    }                                                             \ +    acc[0] = temp_sum;                                            \ +    acc[1] += temp[1];                                            \ +                                                                  \ +    env->active_tc.HI[ac] = acc[1];                               \ +    env->active_tc.LO[ac] = acc[0];                               \ +} + +MAQ_S_L_PW(maq_s_l_pwl, 32); +MAQ_S_L_PW(maq_s_l_pwr, 0); + +#undef MAQ_S_L_PW + +#define DM_OPERATE(name, func, is_add, sigext) \ +void helper_##name(target_ulong rs, target_ulong rt, uint32_t ac,    \ +                  CPUMIPSState *env)                                 \ +{                                                                    \ +    int32_t rs1, rs0;                                                \ +    int32_t rt1, rt0;                                                \ +    int64_t tempBL[2], tempAL[2];                                    \ +    int64_t acc[2];                                                  \ +    int64_t temp[2];                                                 \ +    int64_t temp_sum;                                                \ +                                                                     \ +    temp[0] = 0x00;                                                  \ +    temp[1] = 0x00;                                                  \ +                                                                     \ +    MIPSDSP_SPLIT64_32(rs, rs1, rs0);                                \ +    MIPSDSP_SPLIT64_32(rt, rt1, rt0);                                \ +                                                                     \ +    if (sigext) {                                                    \ +        tempBL[0] = (int64_t)mipsdsp_##func(rs1, rt1);               \ +        tempAL[0] = (int64_t)mipsdsp_##func(rs0, rt0);               \ +                                                                     \ +        if (tempBL[0] >= 0) {                                        \ +            tempBL[1] = 0x0;                                         \ +        } else {                                                     \ +            tempBL[1] = ~0ull;                                       \ +        }                                                            \ +                                                                     \ +        if (tempAL[0] >= 0) {                                        \ +            tempAL[1] = 0x0;                                         \ +        } else {                                                     \ +            tempAL[1] = ~0ull;                                       \ +        }                                                            \ +    } else {                                                         \ +        tempBL[0] = mipsdsp_##func(rs1, rt1);                        \ +        tempAL[0] = mipsdsp_##func(rs0, rt0);                        \ +        tempBL[1] = 0;                                               \ +        tempAL[1] = 0;                                               \ +    }                                                                \ +                                                                     \ +    acc[1] = env->active_tc.HI[ac];                                  \ +    acc[0] = env->active_tc.LO[ac];                                  \ +                                                                     \ +    temp_sum = tempBL[0] + tempAL[0];                                \ +    if (((uint64_t)temp_sum < (uint64_t)tempBL[0]) &&                \ +        ((uint64_t)temp_sum < (uint64_t)tempAL[0])) {                \ +        temp[1] += 1;                                                \ +    }                                                                \ +    temp[0] = temp_sum;                                              \ +    temp[1] += tempBL[1] + tempAL[1];                                \ +                                                                     \ +    if (is_add) {                                                    \ +        temp_sum = acc[0] + temp[0];                                 \ +        if (((uint64_t)temp_sum < (uint64_t)acc[0]) &&               \ +            ((uint64_t)temp_sum < (uint64_t)temp[0])) {              \ +            acc[1] += 1;                                             \ +        }                                                            \ +        temp[0] = temp_sum;                                          \ +        temp[1] = acc[1] + temp[1];                                  \ +    } else {                                                         \ +        temp_sum = acc[0] - temp[0];                                 \ +        if ((uint64_t)temp_sum > (uint64_t)acc[0]) {                 \ +            acc[1] -= 1;                                             \ +        }                                                            \ +        temp[0] = temp_sum;                                          \ +        temp[1] = acc[1] - temp[1];                                  \ +    }                                                                \ +                                                                     \ +    env->active_tc.HI[ac] = temp[1];                                 \ +    env->active_tc.LO[ac] = temp[0];                                 \ +} + +DM_OPERATE(dmadd, mul_i32_i32, 1, 1); +DM_OPERATE(dmaddu, mul_u32_u32, 1, 0); +DM_OPERATE(dmsub, mul_i32_i32, 0, 1); +DM_OPERATE(dmsubu, mul_u32_u32, 0, 0); +#undef DM_OPERATE +#endif + +/** DSP Bit/Manipulation Sub-class insns **/ +target_ulong helper_bitrev(target_ulong rt) +{ +    int32_t temp; +    uint32_t rd; +    int i; + +    temp = rt & MIPSDSP_LO; +    rd = 0; +    for (i = 0; i < 16; i++) { +        rd = (rd << 1) | (temp & 1); +        temp = temp >> 1; +    } + +    return (target_ulong)rd; +} + +#define BIT_INSV(name, posfilter, ret_type)                     \ +target_ulong helper_##name(CPUMIPSState *env, target_ulong rs,  \ +                           target_ulong rt)                     \ +{                                                               \ +    uint32_t pos, size, msb, lsb;                               \ +    uint32_t const sizefilter = 0x3F;                           \ +    target_ulong temp;                                          \ +    target_ulong dspc;                                          \ +                                                                \ +    dspc = env->active_tc.DSPControl;                           \ +                                                                \ +    pos  = dspc & posfilter;                                    \ +    size = (dspc >> 7) & sizefilter;                            \ +                                                                \ +    msb  = pos + size - 1;                                      \ +    lsb  = pos;                                                 \ +                                                                \ +    if (lsb > msb || (msb > TARGET_LONG_BITS)) {                \ +        return rt;                                              \ +    }                                                           \ +                                                                \ +    temp = deposit64(rt, pos, size, rs);                        \ +                                                                \ +    return (target_long)(ret_type)temp;                         \ +} + +BIT_INSV(insv, 0x1F, int32_t); +#ifdef TARGET_MIPS64 +BIT_INSV(dinsv, 0x7F, target_long); +#endif + +#undef BIT_INSV + + +/** DSP Compare-Pick Sub-class insns **/ +#define CMP_HAS_RET(name, func, split_num, filter, bit_size) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt) \ +{                                                       \ +    uint32_t rs_t, rt_t;                                \ +    uint8_t cc;                                         \ +    uint32_t temp = 0;                                  \ +    int i;                                              \ +                                                        \ +    for (i = 0; i < split_num; i++) {                   \ +        rs_t = (rs >> (bit_size * i)) & filter;         \ +        rt_t = (rt >> (bit_size * i)) & filter;         \ +        cc = mipsdsp_##func(rs_t, rt_t);                \ +        temp |= cc << i;                                \ +    }                                                   \ +                                                        \ +    return (target_ulong)temp;                          \ +} + +CMP_HAS_RET(cmpgu_eq_qb, cmpu_eq, 4, MIPSDSP_Q0, 8); +CMP_HAS_RET(cmpgu_lt_qb, cmpu_lt, 4, MIPSDSP_Q0, 8); +CMP_HAS_RET(cmpgu_le_qb, cmpu_le, 4, MIPSDSP_Q0, 8); + +#ifdef TARGET_MIPS64 +CMP_HAS_RET(cmpgu_eq_ob, cmpu_eq, 8, MIPSDSP_Q0, 8); +CMP_HAS_RET(cmpgu_lt_ob, cmpu_lt, 8, MIPSDSP_Q0, 8); +CMP_HAS_RET(cmpgu_le_ob, cmpu_le, 8, MIPSDSP_Q0, 8); +#endif + +#undef CMP_HAS_RET + + +#define CMP_NO_RET(name, func, split_num, filter, bit_size) \ +void helper_##name(target_ulong rs, target_ulong rt,        \ +                            CPUMIPSState *env)              \ +{                                                           \ +    int##bit_size##_t rs_t, rt_t;                           \ +    int##bit_size##_t flag = 0;                             \ +    int##bit_size##_t cc;                                   \ +    int i;                                                  \ +                                                            \ +    for (i = 0; i < split_num; i++) {                       \ +        rs_t = (rs >> (bit_size * i)) & filter;             \ +        rt_t = (rt >> (bit_size * i)) & filter;             \ +                                                            \ +        cc = mipsdsp_##func((int32_t)rs_t, (int32_t)rt_t);  \ +        flag |= cc << i;                                    \ +    }                                                       \ +                                                            \ +    set_DSPControl_24(flag, split_num, env);                \ +} + +CMP_NO_RET(cmpu_eq_qb, cmpu_eq, 4, MIPSDSP_Q0, 8); +CMP_NO_RET(cmpu_lt_qb, cmpu_lt, 4, MIPSDSP_Q0, 8); +CMP_NO_RET(cmpu_le_qb, cmpu_le, 4, MIPSDSP_Q0, 8); + +CMP_NO_RET(cmp_eq_ph, cmp_eq, 2, MIPSDSP_LO, 16); +CMP_NO_RET(cmp_lt_ph, cmp_lt, 2, MIPSDSP_LO, 16); +CMP_NO_RET(cmp_le_ph, cmp_le, 2, MIPSDSP_LO, 16); + +#ifdef TARGET_MIPS64 +CMP_NO_RET(cmpu_eq_ob, cmpu_eq, 8, MIPSDSP_Q0, 8); +CMP_NO_RET(cmpu_lt_ob, cmpu_lt, 8, MIPSDSP_Q0, 8); +CMP_NO_RET(cmpu_le_ob, cmpu_le, 8, MIPSDSP_Q0, 8); + +CMP_NO_RET(cmp_eq_qh, cmp_eq, 4, MIPSDSP_LO, 16); +CMP_NO_RET(cmp_lt_qh, cmp_lt, 4, MIPSDSP_LO, 16); +CMP_NO_RET(cmp_le_qh, cmp_le, 4, MIPSDSP_LO, 16); + +CMP_NO_RET(cmp_eq_pw, cmp_eq, 2, MIPSDSP_LLO, 32); +CMP_NO_RET(cmp_lt_pw, cmp_lt, 2, MIPSDSP_LLO, 32); +CMP_NO_RET(cmp_le_pw, cmp_le, 2, MIPSDSP_LLO, 32); +#endif +#undef CMP_NO_RET + +#if defined(TARGET_MIPS64) + +#define CMPGDU_OB(name) \ +target_ulong helper_cmpgdu_##name##_ob(target_ulong rs, target_ulong rt, \ +                                       CPUMIPSState *env)  \ +{                                                     \ +    int i;                                            \ +    uint8_t rs_t, rt_t;                               \ +    uint32_t cond;                                    \ +                                                      \ +    cond = 0;                                         \ +                                                      \ +    for (i = 0; i < 8; i++) {                         \ +        rs_t = (rs >> (8 * i)) & MIPSDSP_Q0;          \ +        rt_t = (rt >> (8 * i)) & MIPSDSP_Q0;          \ +                                                      \ +        if (mipsdsp_cmpu_##name(rs_t, rt_t)) {        \ +            cond |= 0x01 << i;                        \ +        }                                             \ +    }                                                 \ +                                                      \ +    set_DSPControl_24(cond, 8, env);                  \ +                                                      \ +    return (uint64_t)cond;                            \ +} + +CMPGDU_OB(eq) +CMPGDU_OB(lt) +CMPGDU_OB(le) +#undef CMPGDU_OB +#endif + +#define PICK_INSN(name, split_num, filter, bit_size, ret32bit) \ +target_ulong helper_##name(target_ulong rs, target_ulong rt,   \ +                            CPUMIPSState *env)                 \ +{                                                              \ +    uint32_t rs_t, rt_t;                                       \ +    uint32_t cc;                                               \ +    target_ulong dsp;                                          \ +    int i;                                                     \ +    target_ulong result = 0;                                   \ +                                                               \ +    dsp = env->active_tc.DSPControl;                           \ +    for (i = 0; i < split_num; i++) {                          \ +        rs_t = (rs >> (bit_size * i)) & filter;                \ +        rt_t = (rt >> (bit_size * i)) & filter;                \ +        cc = (dsp >> (24 + i)) & 0x01;                         \ +        cc = cc == 1 ? rs_t : rt_t;                            \ +                                                               \ +        result |= (target_ulong)cc << (bit_size * i);          \ +    }                                                          \ +                                                               \ +    if (ret32bit) {                                            \ +        result = (target_long)(int32_t)(result & MIPSDSP_LLO); \ +    }                                                          \ +                                                               \ +    return result;                                             \ +} + +PICK_INSN(pick_qb, 4, MIPSDSP_Q0, 8, 1); +PICK_INSN(pick_ph, 2, MIPSDSP_LO, 16, 1); + +#ifdef TARGET_MIPS64 +PICK_INSN(pick_ob, 8, MIPSDSP_Q0, 8, 0); +PICK_INSN(pick_qh, 4, MIPSDSP_LO, 16, 0); +PICK_INSN(pick_pw, 2, MIPSDSP_LLO, 32, 0); +#endif +#undef PICK_INSN + +target_ulong helper_packrl_ph(target_ulong rs, target_ulong rt) +{ +    uint32_t rsl, rth; + +    rsl =  rs & MIPSDSP_LO; +    rth = (rt & MIPSDSP_HI) >> 16; + +    return (target_long)(int32_t)((rsl << 16) | rth); +} + +#if defined(TARGET_MIPS64) +target_ulong helper_packrl_pw(target_ulong rs, target_ulong rt) +{ +    uint32_t rs0, rt1; + +    rs0 = rs & MIPSDSP_LLO; +    rt1 = (rt >> 32) & MIPSDSP_LLO; + +    return ((uint64_t)rs0 << 32) | (uint64_t)rt1; +} +#endif + +/** DSP Accumulator and DSPControl Access Sub-class insns **/ +target_ulong helper_extr_w(target_ulong ac, target_ulong shift, +                           CPUMIPSState *env) +{ +    int32_t tempI; +    int64_t tempDL[2]; + +    shift = shift & 0x1F; + +    mipsdsp_rndrashift_short_acc(tempDL, ac, shift, env); +    if ((tempDL[1] != 0 || (tempDL[0] & MIPSDSP_LHI) != 0) && +        (tempDL[1] != 1 || (tempDL[0] & MIPSDSP_LHI) != MIPSDSP_LHI)) { +        set_DSPControl_overflow_flag(1, 23, env); +    } + +    tempI = (tempDL[0] >> 1) & MIPSDSP_LLO; + +    tempDL[0] += 1; +    if (tempDL[0] == 0) { +        tempDL[1] += 1; +    } + +    if (((tempDL[1] & 0x01) != 0 || (tempDL[0] & MIPSDSP_LHI) != 0) && +        ((tempDL[1] & 0x01) != 1 || (tempDL[0] & MIPSDSP_LHI) != MIPSDSP_LHI)) { +        set_DSPControl_overflow_flag(1, 23, env); +    } + +    return (target_long)tempI; +} + +target_ulong helper_extr_r_w(target_ulong ac, target_ulong shift, +                             CPUMIPSState *env) +{ +    int64_t tempDL[2]; + +    shift = shift & 0x1F; + +    mipsdsp_rndrashift_short_acc(tempDL, ac, shift, env); +    if ((tempDL[1] != 0 || (tempDL[0] & MIPSDSP_LHI) != 0) && +        (tempDL[1] != 1 || (tempDL[0] & MIPSDSP_LHI) != MIPSDSP_LHI)) { +        set_DSPControl_overflow_flag(1, 23, env); +    } + +    tempDL[0] += 1; +    if (tempDL[0] == 0) { +        tempDL[1] += 1; +    } + +    if (((tempDL[1] & 0x01) != 0 || (tempDL[0] & MIPSDSP_LHI) != 0) && +        ((tempDL[1] & 0x01) != 1 || (tempDL[0] & MIPSDSP_LHI) != MIPSDSP_LHI)) { +        set_DSPControl_overflow_flag(1, 23, env); +    } + +    return (target_long)(int32_t)(tempDL[0] >> 1); +} + +target_ulong helper_extr_rs_w(target_ulong ac, target_ulong shift, +                              CPUMIPSState *env) +{ +    int32_t tempI, temp64; +    int64_t tempDL[2]; + +    shift = shift & 0x1F; + +    mipsdsp_rndrashift_short_acc(tempDL, ac, shift, env); +    if ((tempDL[1] != 0 || (tempDL[0] & MIPSDSP_LHI) != 0) && +        (tempDL[1] != 1 || (tempDL[0] & MIPSDSP_LHI) != MIPSDSP_LHI)) { +        set_DSPControl_overflow_flag(1, 23, env); +    } +    tempDL[0] += 1; +    if (tempDL[0] == 0) { +        tempDL[1] += 1; +    } +    tempI = tempDL[0] >> 1; + +    if (((tempDL[1] & 0x01) != 0 || (tempDL[0] & MIPSDSP_LHI) != 0) && +        ((tempDL[1] & 0x01) != 1 || (tempDL[0] & MIPSDSP_LHI) != MIPSDSP_LHI)) { +        temp64 = tempDL[1] & 0x01; +        if (temp64 == 0) { +            tempI = 0x7FFFFFFF; +        } else { +            tempI = 0x80000000; +        } +        set_DSPControl_overflow_flag(1, 23, env); +    } + +    return (target_long)tempI; +} + +#if defined(TARGET_MIPS64) +target_ulong helper_dextr_w(target_ulong ac, target_ulong shift, +                            CPUMIPSState *env) +{ +    uint64_t temp[3]; + +    shift = shift & 0x3F; + +    mipsdsp_rndrashift_acc(temp, ac, shift, env); + +    return (int64_t)(int32_t)(temp[0] >> 1); +} + +target_ulong helper_dextr_r_w(target_ulong ac, target_ulong shift, +                              CPUMIPSState *env) +{ +    uint64_t temp[3]; +    uint32_t temp128; + +    shift = shift & 0x3F; +    mipsdsp_rndrashift_acc(temp, ac, shift, env); + +    temp[0] += 1; +    if (temp[0] == 0) { +        temp[1] += 1; +        if (temp[1] == 0) { +            temp[2] += 1; +        } +    } + +    temp128 = temp[2] & 0x01; + +    if ((temp128 != 0 || temp[1] != 0) && +       (temp128 != 1 || temp[1] != ~0ull)) { +        set_DSPControl_overflow_flag(1, 23, env); +    } + +    return (int64_t)(int32_t)(temp[0] >> 1); +} + +target_ulong helper_dextr_rs_w(target_ulong ac, target_ulong shift, +                               CPUMIPSState *env) +{ +    uint64_t temp[3]; +    uint32_t temp128; + +    shift = shift & 0x3F; +    mipsdsp_rndrashift_acc(temp, ac, shift, env); + +    temp[0] += 1; +    if (temp[0] == 0) { +        temp[1] += 1; +        if (temp[1] == 0) { +            temp[2] += 1; +        } +    } + +    temp128 = temp[2] & 0x01; + +    if ((temp128 != 0 || temp[1] != 0) && +       (temp128 != 1 || temp[1] != ~0ull)) { +        if (temp128 == 0) { +            temp[0] = 0x0FFFFFFFF; +        } else { +            temp[0] = 0x0100000000ULL; +        } +        set_DSPControl_overflow_flag(1, 23, env); +    } + +    return (int64_t)(int32_t)(temp[0] >> 1); +} + +target_ulong helper_dextr_l(target_ulong ac, target_ulong shift, +                            CPUMIPSState *env) +{ +    uint64_t temp[3]; +    target_ulong result; + +    shift = shift & 0x3F; + +    mipsdsp_rndrashift_acc(temp, ac, shift, env); +    result = (temp[1] << 63) | (temp[0] >> 1); + +    return result; +} + +target_ulong helper_dextr_r_l(target_ulong ac, target_ulong shift, +                              CPUMIPSState *env) +{ +    uint64_t temp[3]; +    uint32_t temp128; +    target_ulong result; + +    shift = shift & 0x3F; +    mipsdsp_rndrashift_acc(temp, ac, shift, env); + +    temp[0] += 1; +    if (temp[0] == 0) { +        temp[1] += 1; +        if (temp[1] == 0) { +            temp[2] += 1; +        } +    } + +    temp128 = temp[2] & 0x01; + +    if ((temp128 != 0 || temp[1] != 0) && +       (temp128 != 1 || temp[1] != ~0ull)) { +        set_DSPControl_overflow_flag(1, 23, env); +    } + +    result = (temp[1] << 63) | (temp[0] >> 1); + +    return result; +} + +target_ulong helper_dextr_rs_l(target_ulong ac, target_ulong shift, +                               CPUMIPSState *env) +{ +    uint64_t temp[3]; +    uint32_t temp128; +    target_ulong result; + +    shift = shift & 0x3F; +    mipsdsp_rndrashift_acc(temp, ac, shift, env); + +    temp[0] += 1; +    if (temp[0] == 0) { +        temp[1] += 1; +        if (temp[1] == 0) { +            temp[2] += 1; +        } +    } + +    temp128 = temp[2] & 0x01; + +    if ((temp128 != 0 || temp[1] != 0) && +       (temp128 != 1 || temp[1] != ~0ull)) { +        if (temp128 == 0) { +            temp[1] &= ~0x00ull - 1; +            temp[0] |= ~0x00ull - 1; +        } else { +            temp[1] |= 0x01; +            temp[0] &= 0x01; +        } +        set_DSPControl_overflow_flag(1, 23, env); +    } +    result = (temp[1] << 63) | (temp[0] >> 1); + +    return result; +} +#endif + +target_ulong helper_extr_s_h(target_ulong ac, target_ulong shift, +                             CPUMIPSState *env) +{ +    int64_t temp, acc; + +    shift = shift & 0x1F; + +    acc = ((int64_t)env->active_tc.HI[ac] << 32) | +          ((int64_t)env->active_tc.LO[ac] & 0xFFFFFFFF); + +    temp = acc >> shift; + +    if (temp > (int64_t)0x7FFF) { +        temp = 0x00007FFF; +        set_DSPControl_overflow_flag(1, 23, env); +    } else if (temp < (int64_t)0xFFFFFFFFFFFF8000ULL) { +        temp = 0xFFFF8000; +        set_DSPControl_overflow_flag(1, 23, env); +    } + +    return (target_long)(int32_t)(temp & 0xFFFFFFFF); +} + + +#if defined(TARGET_MIPS64) +target_ulong helper_dextr_s_h(target_ulong ac, target_ulong shift, +                              CPUMIPSState *env) +{ +    int64_t temp[2]; +    uint32_t temp127; + +    shift = shift & 0x1F; + +    mipsdsp_rashift_acc((uint64_t *)temp, ac, shift, env); + +    temp127 = (temp[1] >> 63) & 0x01; + +    if ((temp127 == 0) && (temp[1] > 0 || temp[0] > 32767)) { +        temp[0] &= 0xFFFF0000; +        temp[0] |= 0x00007FFF; +        set_DSPControl_overflow_flag(1, 23, env); +    } else if ((temp127 == 1) && +            (temp[1] < 0xFFFFFFFFFFFFFFFFll +             || temp[0] < 0xFFFFFFFFFFFF1000ll)) { +        temp[0] &= 0xFFFF0000; +        temp[0] |= 0x00008000; +        set_DSPControl_overflow_flag(1, 23, env); +    } + +    return (int64_t)(int16_t)(temp[0] & MIPSDSP_LO); +} + +#endif + +target_ulong helper_extp(target_ulong ac, target_ulong size, CPUMIPSState *env) +{ +    int32_t start_pos; +    int sub; +    uint32_t temp; +    uint64_t acc; + +    size = size & 0x1F; + +    temp = 0; +    start_pos = get_DSPControl_pos(env); +    sub = start_pos - (size + 1); +    if (sub >= -1) { +        acc = ((uint64_t)env->active_tc.HI[ac] << 32) | +              ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO); +        temp = (acc >> (start_pos - size)) & (~0U >> (31 - size)); +        set_DSPControl_efi(0, env); +    } else { +        set_DSPControl_efi(1, env); +    } + +    return (target_ulong)temp; +} + +target_ulong helper_extpdp(target_ulong ac, target_ulong size, +                           CPUMIPSState *env) +{ +    int32_t start_pos; +    int sub; +    uint32_t temp; +    uint64_t acc; + +    size = size & 0x1F; +    temp = 0; +    start_pos = get_DSPControl_pos(env); +    sub = start_pos - (size + 1); +    if (sub >= -1) { +        acc  = ((uint64_t)env->active_tc.HI[ac] << 32) | +               ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO); +        temp = extract64(acc, start_pos - size, size + 1); + +        set_DSPControl_pos(sub, env); +        set_DSPControl_efi(0, env); +    } else { +        set_DSPControl_efi(1, env); +    } + +    return (target_ulong)temp; +} + + +#if defined(TARGET_MIPS64) +target_ulong helper_dextp(target_ulong ac, target_ulong size, CPUMIPSState *env) +{ +    int start_pos; +    int len; +    int sub; +    uint64_t tempB, tempA; +    uint64_t temp; + +    temp = 0; + +    size = size & 0x3F; +    start_pos = get_DSPControl_pos(env); +    len = start_pos - size; +    tempB = env->active_tc.HI[ac]; +    tempA = env->active_tc.LO[ac]; + +    sub = start_pos - (size + 1); + +    if (sub >= -1) { +        temp = (tempB << (64 - len)) | (tempA >> len); +        temp = temp & ((0x01 << (size + 1)) - 1); +        set_DSPControl_efi(0, env); +    } else { +        set_DSPControl_efi(1, env); +    } + +    return temp; +} + +target_ulong helper_dextpdp(target_ulong ac, target_ulong size, +                            CPUMIPSState *env) +{ +    int start_pos; +    int len; +    int sub; +    uint64_t tempB, tempA; +    uint64_t temp; + +    temp = 0; +    size = size & 0x3F; +    start_pos = get_DSPControl_pos(env); +    len = start_pos - size; +    tempB = env->active_tc.HI[ac]; +    tempA = env->active_tc.LO[ac]; + +    sub = start_pos - (size + 1); + +    if (sub >= -1) { +        temp = (tempB << (64 - len)) | (tempA >> len); +        temp = temp & ((0x01 << (size + 1)) - 1); +        set_DSPControl_pos(sub, env); +        set_DSPControl_efi(0, env); +    } else { +        set_DSPControl_efi(1, env); +    } + +    return temp; +} + +#endif + +void helper_shilo(target_ulong ac, target_ulong rs, CPUMIPSState *env) +{ +    int8_t  rs5_0; +    uint64_t temp, acc; + +    rs5_0 = rs & 0x3F; +    rs5_0 = (int8_t)(rs5_0 << 2) >> 2; + +    if (unlikely(rs5_0 == 0)) { +        return; +    } + +    acc   = (((uint64_t)env->active_tc.HI[ac] << 32) & MIPSDSP_LHI) | +            ((uint64_t)env->active_tc.LO[ac] & MIPSDSP_LLO); + +    if (rs5_0 > 0) { +        temp = acc >> rs5_0; +    } else { +        temp = acc << -rs5_0; +    } + +    env->active_tc.HI[ac] = (target_ulong)(int32_t)((temp & MIPSDSP_LHI) >> 32); +    env->active_tc.LO[ac] = (target_ulong)(int32_t)(temp & MIPSDSP_LLO); +} + +#if defined(TARGET_MIPS64) +void helper_dshilo(target_ulong shift, target_ulong ac, CPUMIPSState *env) +{ +    int8_t shift_t; +    uint64_t tempB, tempA; + +    shift_t = (int8_t)(shift << 1) >> 1; + +    tempB = env->active_tc.HI[ac]; +    tempA = env->active_tc.LO[ac]; + +    if (shift_t != 0) { +        if (shift_t >= 0) { +            tempA = (tempB << (64 - shift_t)) | (tempA >> shift_t); +            tempB = tempB >> shift_t; +        } else { +            shift_t = -shift_t; +            tempB = (tempB << shift_t) | (tempA >> (64 - shift_t)); +            tempA = tempA << shift_t; +        } +    } + +    env->active_tc.HI[ac] = tempB; +    env->active_tc.LO[ac] = tempA; +} + +#endif +void helper_mthlip(target_ulong ac, target_ulong rs, CPUMIPSState *env) +{ +    int32_t tempA, tempB, pos; + +    tempA = rs; +    tempB = env->active_tc.LO[ac]; +    env->active_tc.HI[ac] = (target_long)tempB; +    env->active_tc.LO[ac] = (target_long)tempA; +    pos = get_DSPControl_pos(env); + +    if (pos > 32) { +        return; +    } else { +        set_DSPControl_pos(pos + 32, env); +    } +} + +#if defined(TARGET_MIPS64) +void helper_dmthlip(target_ulong rs, target_ulong ac, CPUMIPSState *env) +{ +    uint8_t ac_t; +    uint8_t pos; +    uint64_t tempB, tempA; + +    ac_t = ac & 0x3; + +    tempA = rs; +    tempB = env->active_tc.LO[ac_t]; + +    env->active_tc.HI[ac_t] = tempB; +    env->active_tc.LO[ac_t] = tempA; + +    pos = get_DSPControl_pos(env); + +    if (pos <= 64) { +        pos = pos + 64; +        set_DSPControl_pos(pos, env); +    } +} +#endif + +void cpu_wrdsp(uint32_t rs, uint32_t mask_num, CPUMIPSState *env) +{ +    uint8_t  mask[6]; +    uint8_t  i; +    uint32_t newbits, overwrite; +    target_ulong dsp; + +    newbits   = 0x00; +    overwrite = 0xFFFFFFFF; +    dsp = env->active_tc.DSPControl; + +    for (i = 0; i < 6; i++) { +        mask[i] = (mask_num >> i) & 0x01; +    } + +    if (mask[0] == 1) { +#if defined(TARGET_MIPS64) +        overwrite &= 0xFFFFFF80; +        newbits   &= 0xFFFFFF80; +        newbits   |= 0x0000007F & rs; +#else +        overwrite &= 0xFFFFFFC0; +        newbits   &= 0xFFFFFFC0; +        newbits   |= 0x0000003F & rs; +#endif +    } + +    if (mask[1] == 1) { +        overwrite &= 0xFFFFE07F; +        newbits   &= 0xFFFFE07F; +        newbits   |= 0x00001F80 & rs; +    } + +    if (mask[2] == 1) { +        overwrite &= 0xFFFFDFFF; +        newbits   &= 0xFFFFDFFF; +        newbits   |= 0x00002000 & rs; +    } + +    if (mask[3] == 1) { +        overwrite &= 0xFF00FFFF; +        newbits   &= 0xFF00FFFF; +        newbits   |= 0x00FF0000 & rs; +    } + +    if (mask[4] == 1) { +        overwrite &= 0x00FFFFFF; +        newbits   &= 0x00FFFFFF; +#if defined(TARGET_MIPS64) +        newbits   |= 0xFF000000 & rs; +#else +        newbits   |= 0x0F000000 & rs; +#endif +    } + +    if (mask[5] == 1) { +        overwrite &= 0xFFFFBFFF; +        newbits   &= 0xFFFFBFFF; +        newbits   |= 0x00004000 & rs; +    } + +    dsp = dsp & overwrite; +    dsp = dsp | newbits; +    env->active_tc.DSPControl = dsp; +} + +void helper_wrdsp(target_ulong rs, target_ulong mask_num, CPUMIPSState *env) +{ +    cpu_wrdsp(rs, mask_num, env); +} + +uint32_t cpu_rddsp(uint32_t mask_num, CPUMIPSState *env) +{ +    uint8_t  mask[6]; +    uint32_t ruler, i; +    target_ulong temp; +    target_ulong dsp; + +    ruler = 0x01; +    for (i = 0; i < 6; i++) { +        mask[i] = (mask_num & ruler) >> i ; +        ruler = ruler << 1; +    } + +    temp  = 0x00; +    dsp = env->active_tc.DSPControl; + +    if (mask[0] == 1) { +#if defined(TARGET_MIPS64) +        temp |= dsp & 0x7F; +#else +        temp |= dsp & 0x3F; +#endif +    } + +    if (mask[1] == 1) { +        temp |= dsp & 0x1F80; +    } + +    if (mask[2] == 1) { +        temp |= dsp & 0x2000; +    } + +    if (mask[3] == 1) { +        temp |= dsp & 0x00FF0000; +    } + +    if (mask[4] == 1) { +#if defined(TARGET_MIPS64) +        temp |= dsp & 0xFF000000; +#else +        temp |= dsp & 0x0F000000; +#endif +    } + +    if (mask[5] == 1) { +        temp |= dsp & 0x4000; +    } + +    return temp; +} + +target_ulong helper_rddsp(target_ulong mask_num, CPUMIPSState *env) +{ +    return cpu_rddsp(mask_num, env); +} + + +#undef MIPSDSP_LHI +#undef MIPSDSP_LLO +#undef MIPSDSP_HI +#undef MIPSDSP_LO +#undef MIPSDSP_Q3 +#undef MIPSDSP_Q2 +#undef MIPSDSP_Q1 +#undef MIPSDSP_Q0 + +#undef MIPSDSP_SPLIT32_8 +#undef MIPSDSP_SPLIT32_16 + +#undef MIPSDSP_RETURN32_8 +#undef MIPSDSP_RETURN32_16 + +#ifdef TARGET_MIPS64 +#undef MIPSDSP_SPLIT64_16 +#undef MIPSDSP_SPLIT64_32 +#undef MIPSDSP_RETURN64_16 +#undef MIPSDSP_RETURN64_32 +#endif diff --git a/target-mips/gdbstub.c b/target-mips/gdbstub.c new file mode 100644 index 00000000..9845d880 --- /dev/null +++ b/target-mips/gdbstub.c @@ -0,0 +1,150 @@ +/* + * MIPS 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" + +int mips_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; + +    if (n < 32) { +        return gdb_get_regl(mem_buf, env->active_tc.gpr[n]); +    } +    if (env->CP0_Config1 & (1 << CP0C1_FP) && n >= 38 && n < 72) { +        switch (n) { +        case 70: +            return gdb_get_regl(mem_buf, (int32_t)env->active_fpu.fcr31); +        case 71: +            return gdb_get_regl(mem_buf, (int32_t)env->active_fpu.fcr0); +        default: +            if (env->CP0_Status & (1 << CP0St_FR)) { +                return gdb_get_regl(mem_buf, +                    env->active_fpu.fpr[n - 38].d); +            } else { +                return gdb_get_regl(mem_buf, +                    env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX]); +            } +        } +    } +    switch (n) { +    case 32: +        return gdb_get_regl(mem_buf, (int32_t)env->CP0_Status); +    case 33: +        return gdb_get_regl(mem_buf, env->active_tc.LO[0]); +    case 34: +        return gdb_get_regl(mem_buf, env->active_tc.HI[0]); +    case 35: +        return gdb_get_regl(mem_buf, env->CP0_BadVAddr); +    case 36: +        return gdb_get_regl(mem_buf, (int32_t)env->CP0_Cause); +    case 37: +        return gdb_get_regl(mem_buf, env->active_tc.PC | +                                     !!(env->hflags & MIPS_HFLAG_M16)); +    case 72: +        return gdb_get_regl(mem_buf, 0); /* fp */ +    case 89: +        return gdb_get_regl(mem_buf, (int32_t)env->CP0_PRid); +    default: +        if (n > 89) { +            return 0; +        } +        /* 16 embedded regs.  */ +        return gdb_get_regl(mem_buf, 0); +    } + +    return 0; +} + +int mips_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    target_ulong tmp; + +    tmp = ldtul_p(mem_buf); + +    if (n < 32) { +        env->active_tc.gpr[n] = tmp; +        return sizeof(target_ulong); +    } +    if (env->CP0_Config1 & (1 << CP0C1_FP) && n >= 38 && n < 72) { +        switch (n) { +        case 70: +            env->active_fpu.fcr31 = tmp & 0xFF83FFFF; +            /* set rounding mode */ +            restore_rounding_mode(env); +            /* set flush-to-zero mode */ +            restore_flush_mode(env); +            break; +        case 71: +            /* FIR is read-only.  Ignore writes.  */ +            break; +        default: +            if (env->CP0_Status & (1 << CP0St_FR)) { +                env->active_fpu.fpr[n - 38].d = tmp; +            } else { +                env->active_fpu.fpr[n - 38].w[FP_ENDIAN_IDX] = tmp; +            } +            break; +        } +        return sizeof(target_ulong); +    } +    switch (n) { +    case 32: +#ifndef CONFIG_USER_ONLY +        cpu_mips_store_status(env, tmp); +#endif +        break; +    case 33: +        env->active_tc.LO[0] = tmp; +        break; +    case 34: +        env->active_tc.HI[0] = tmp; +        break; +    case 35: +        env->CP0_BadVAddr = tmp; +        break; +    case 36: +#ifndef CONFIG_USER_ONLY +        cpu_mips_store_cause(env, tmp); +#endif +        break; +    case 37: +        env->active_tc.PC = tmp & ~(target_ulong)1; +        if (tmp & 1) { +            env->hflags |= MIPS_HFLAG_M16; +        } else { +            env->hflags &= ~(MIPS_HFLAG_M16); +        } +        break; +    case 72: /* fp, ignored */ +        break; +    default: +        if (n > 89) { +            return 0; +        } +        /* Other registers are readonly.  Ignore writes.  */ +        break; +    } + +    return sizeof(target_ulong); +} diff --git a/target-mips/helper.c b/target-mips/helper.c new file mode 100644 index 00000000..04ba19fd --- /dev/null +++ b/target-mips/helper.c @@ -0,0 +1,840 @@ +/* + *  MIPS emulation helpers for qemu. + * + *  Copyright (c) 2004-2005 Jocelyn Mayer + * + * 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" +#include "sysemu/kvm.h" +#include "exec/cpu_ldst.h" + +enum { +    TLBRET_XI = -6, +    TLBRET_RI = -5, +    TLBRET_DIRTY = -4, +    TLBRET_INVALID = -3, +    TLBRET_NOMATCH = -2, +    TLBRET_BADADDR = -1, +    TLBRET_MATCH = 0 +}; + +#if !defined(CONFIG_USER_ONLY) + +/* no MMU emulation */ +int no_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, +                        target_ulong address, int rw, int access_type) +{ +    *physical = address; +    *prot = PAGE_READ | PAGE_WRITE; +    return TLBRET_MATCH; +} + +/* fixed mapping MMU emulation */ +int fixed_mmu_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, +                           target_ulong address, int rw, int access_type) +{ +    if (address <= (int32_t)0x7FFFFFFFUL) { +        if (!(env->CP0_Status & (1 << CP0St_ERL))) +            *physical = address + 0x40000000UL; +        else +            *physical = address; +    } else if (address <= (int32_t)0xBFFFFFFFUL) +        *physical = address & 0x1FFFFFFF; +    else +        *physical = address; + +    *prot = PAGE_READ | PAGE_WRITE; +    return TLBRET_MATCH; +} + +/* MIPS32/MIPS64 R4000-style MMU emulation */ +int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot, +                     target_ulong address, int rw, int access_type) +{ +    uint8_t ASID = env->CP0_EntryHi & 0xFF; +    int i; + +    for (i = 0; i < env->tlb->tlb_in_use; i++) { +        r4k_tlb_t *tlb = &env->tlb->mmu.r4k.tlb[i]; +        /* 1k pages are not supported. */ +        target_ulong mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); +        target_ulong tag = address & ~mask; +        target_ulong VPN = tlb->VPN & ~mask; +#if defined(TARGET_MIPS64) +        tag &= env->SEGMask; +#endif + +        /* Check ASID, virtual page number & size */ +        if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag && !tlb->EHINV) { +            /* TLB match */ +            int n = !!(address & mask & ~(mask >> 1)); +            /* Check access rights */ +            if (!(n ? tlb->V1 : tlb->V0)) { +                return TLBRET_INVALID; +            } +            if (rw == MMU_INST_FETCH && (n ? tlb->XI1 : tlb->XI0)) { +                return TLBRET_XI; +            } +            if (rw == MMU_DATA_LOAD && (n ? tlb->RI1 : tlb->RI0)) { +                return TLBRET_RI; +            } +            if (rw != MMU_DATA_STORE || (n ? tlb->D1 : tlb->D0)) { +                *physical = tlb->PFN[n] | (address & (mask >> 1)); +                *prot = PAGE_READ; +                if (n ? tlb->D1 : tlb->D0) +                    *prot |= PAGE_WRITE; +                return TLBRET_MATCH; +            } +            return TLBRET_DIRTY; +        } +    } +    return TLBRET_NOMATCH; +} + +static int get_physical_address (CPUMIPSState *env, hwaddr *physical, +                                int *prot, target_ulong real_address, +                                int rw, int access_type) +{ +    /* User mode can only access useg/xuseg */ +    int user_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM; +    int supervisor_mode = (env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_SM; +    int kernel_mode = !user_mode && !supervisor_mode; +#if defined(TARGET_MIPS64) +    int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; +    int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; +    int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; +#endif +    int ret = TLBRET_MATCH; +    /* effective address (modified for KVM T&E kernel segments) */ +    target_ulong address = real_address; + +#if 0 +    qemu_log("user mode %d h %08x\n", user_mode, env->hflags); +#endif + +#define USEG_LIMIT      0x7FFFFFFFUL +#define KSEG0_BASE      0x80000000UL +#define KSEG1_BASE      0xA0000000UL +#define KSEG2_BASE      0xC0000000UL +#define KSEG3_BASE      0xE0000000UL + +#define KVM_KSEG0_BASE  0x40000000UL +#define KVM_KSEG2_BASE  0x60000000UL + +    if (kvm_enabled()) { +        /* KVM T&E adds guest kernel segments in useg */ +        if (real_address >= KVM_KSEG0_BASE) { +            if (real_address < KVM_KSEG2_BASE) { +                /* kseg0 */ +                address += KSEG0_BASE - KVM_KSEG0_BASE; +            } else if (real_address <= USEG_LIMIT) { +                /* kseg2/3 */ +                address += KSEG2_BASE - KVM_KSEG2_BASE; +            } +        } +    } + +    if (address <= USEG_LIMIT) { +        /* useg */ +        if (env->CP0_Status & (1 << CP0St_ERL)) { +            *physical = address & 0xFFFFFFFF; +            *prot = PAGE_READ | PAGE_WRITE; +        } else { +            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); +        } +#if defined(TARGET_MIPS64) +    } else if (address < 0x4000000000000000ULL) { +        /* xuseg */ +        if (UX && address <= (0x3FFFFFFFFFFFFFFFULL & env->SEGMask)) { +            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); +        } else { +            ret = TLBRET_BADADDR; +        } +    } else if (address < 0x8000000000000000ULL) { +        /* xsseg */ +        if ((supervisor_mode || kernel_mode) && +            SX && address <= (0x7FFFFFFFFFFFFFFFULL & env->SEGMask)) { +            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); +        } else { +            ret = TLBRET_BADADDR; +        } +    } else if (address < 0xC000000000000000ULL) { +        /* xkphys */ +        if (kernel_mode && KX && +            (address & 0x07FFFFFFFFFFFFFFULL) <= env->PAMask) { +            *physical = address & env->PAMask; +            *prot = PAGE_READ | PAGE_WRITE; +        } else { +            ret = TLBRET_BADADDR; +        } +    } else if (address < 0xFFFFFFFF80000000ULL) { +        /* xkseg */ +        if (kernel_mode && KX && +            address <= (0xFFFFFFFF7FFFFFFFULL & env->SEGMask)) { +            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); +        } else { +            ret = TLBRET_BADADDR; +        } +#endif +    } else if (address < (int32_t)KSEG1_BASE) { +        /* kseg0 */ +        if (kernel_mode) { +            *physical = address - (int32_t)KSEG0_BASE; +            *prot = PAGE_READ | PAGE_WRITE; +        } else { +            ret = TLBRET_BADADDR; +        } +    } else if (address < (int32_t)KSEG2_BASE) { +        /* kseg1 */ +        if (kernel_mode) { +            *physical = address - (int32_t)KSEG1_BASE; +            *prot = PAGE_READ | PAGE_WRITE; +        } else { +            ret = TLBRET_BADADDR; +        } +    } else if (address < (int32_t)KSEG3_BASE) { +        /* sseg (kseg2) */ +        if (supervisor_mode || kernel_mode) { +            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); +        } else { +            ret = TLBRET_BADADDR; +        } +    } else { +        /* kseg3 */ +        /* XXX: debug segment is not emulated */ +        if (kernel_mode) { +            ret = env->tlb->map_address(env, physical, prot, real_address, rw, access_type); +        } else { +            ret = TLBRET_BADADDR; +        } +    } +#if 0 +    qemu_log(TARGET_FMT_lx " %d %d => %" HWADDR_PRIx " %d (%d)\n", +            address, rw, access_type, *physical, *prot, ret); +#endif + +    return ret; +} +#endif + +static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, +                                int rw, int tlb_error) +{ +    CPUState *cs = CPU(mips_env_get_cpu(env)); +    int exception = 0, error_code = 0; + +    if (rw == MMU_INST_FETCH) { +        error_code |= EXCP_INST_NOTAVAIL; +    } + +    switch (tlb_error) { +    default: +    case TLBRET_BADADDR: +        /* Reference to kernel address from user mode or supervisor mode */ +        /* Reference to supervisor address from user mode */ +        if (rw == MMU_DATA_STORE) { +            exception = EXCP_AdES; +        } else { +            exception = EXCP_AdEL; +        } +        break; +    case TLBRET_NOMATCH: +        /* No TLB match for a mapped address */ +        if (rw == MMU_DATA_STORE) { +            exception = EXCP_TLBS; +        } else { +            exception = EXCP_TLBL; +        } +        error_code |= EXCP_TLB_NOMATCH; +        break; +    case TLBRET_INVALID: +        /* TLB match with no valid bit */ +        if (rw == MMU_DATA_STORE) { +            exception = EXCP_TLBS; +        } else { +            exception = EXCP_TLBL; +        } +        break; +    case TLBRET_DIRTY: +        /* TLB match but 'D' bit is cleared */ +        exception = EXCP_LTLBL; +        break; +    case TLBRET_XI: +        /* Execute-Inhibit Exception */ +        if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { +            exception = EXCP_TLBXI; +        } else { +            exception = EXCP_TLBL; +        } +        break; +    case TLBRET_RI: +        /* Read-Inhibit Exception */ +        if (env->CP0_PageGrain & (1 << CP0PG_IEC)) { +            exception = EXCP_TLBRI; +        } else { +            exception = EXCP_TLBL; +        } +        break; +    } +    /* Raise exception */ +    env->CP0_BadVAddr = address; +    env->CP0_Context = (env->CP0_Context & ~0x007fffff) | +                       ((address >> 9) & 0x007ffff0); +    env->CP0_EntryHi = +        (env->CP0_EntryHi & 0xFF) | (address & (TARGET_PAGE_MASK << 1)); +#if defined(TARGET_MIPS64) +    env->CP0_EntryHi &= env->SEGMask; +    env->CP0_XContext = (env->CP0_XContext & ((~0ULL) << (env->SEGBITS - 7))) | +                        ((address & 0xC00000000000ULL) >> (55 - env->SEGBITS)) | +                        ((address & ((1ULL << env->SEGBITS) - 1) & 0xFFFFFFFFFFFFE000ULL) >> 9); +#endif +    cs->exception_index = exception; +    env->error_code = error_code; +} + +#if !defined(CONFIG_USER_ONLY) +hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    hwaddr phys_addr; +    int prot; + +    if (get_physical_address(&cpu->env, &phys_addr, &prot, addr, 0, +                             ACCESS_INT) != 0) { +        return -1; +    } +    return phys_addr; +} +#endif + +int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, +                              int mmu_idx) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +#if !defined(CONFIG_USER_ONLY) +    hwaddr physical; +    int prot; +    int access_type; +#endif +    int ret = 0; + +#if 0 +    log_cpu_state(cs, 0); +#endif +    qemu_log_mask(CPU_LOG_MMU, +              "%s pc " TARGET_FMT_lx " ad %" VADDR_PRIx " rw %d mmu_idx %d\n", +              __func__, env->active_tc.PC, address, rw, mmu_idx); + +    /* data access */ +#if !defined(CONFIG_USER_ONLY) +    /* XXX: put correct access by using cpu_restore_state() +       correctly */ +    access_type = ACCESS_INT; +    ret = get_physical_address(env, &physical, &prot, +                               address, rw, access_type); +    qemu_log_mask(CPU_LOG_MMU, +             "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx +             " prot %d\n", +             __func__, address, ret, physical, prot); +    if (ret == TLBRET_MATCH) { +        tlb_set_page(cs, address & TARGET_PAGE_MASK, +                     physical & TARGET_PAGE_MASK, prot | PAGE_EXEC, +                     mmu_idx, TARGET_PAGE_SIZE); +        ret = 0; +    } else if (ret < 0) +#endif +    { +        raise_mmu_exception(env, address, rw, ret); +        ret = 1; +    } + +    return ret; +} + +#if !defined(CONFIG_USER_ONLY) +hwaddr cpu_mips_translate_address(CPUMIPSState *env, target_ulong address, int rw) +{ +    hwaddr physical; +    int prot; +    int access_type; +    int ret = 0; + +    /* data access */ +    access_type = ACCESS_INT; +    ret = get_physical_address(env, &physical, &prot, +                               address, rw, access_type); +    if (ret != TLBRET_MATCH) { +        raise_mmu_exception(env, address, rw, ret); +        return -1LL; +    } else { +        return physical; +    } +} + +static const char * const excp_names[EXCP_LAST + 1] = { +    [EXCP_RESET] = "reset", +    [EXCP_SRESET] = "soft reset", +    [EXCP_DSS] = "debug single step", +    [EXCP_DINT] = "debug interrupt", +    [EXCP_NMI] = "non-maskable interrupt", +    [EXCP_MCHECK] = "machine check", +    [EXCP_EXT_INTERRUPT] = "interrupt", +    [EXCP_DFWATCH] = "deferred watchpoint", +    [EXCP_DIB] = "debug instruction breakpoint", +    [EXCP_IWATCH] = "instruction fetch watchpoint", +    [EXCP_AdEL] = "address error load", +    [EXCP_AdES] = "address error store", +    [EXCP_TLBF] = "TLB refill", +    [EXCP_IBE] = "instruction bus error", +    [EXCP_DBp] = "debug breakpoint", +    [EXCP_SYSCALL] = "syscall", +    [EXCP_BREAK] = "break", +    [EXCP_CpU] = "coprocessor unusable", +    [EXCP_RI] = "reserved instruction", +    [EXCP_OVERFLOW] = "arithmetic overflow", +    [EXCP_TRAP] = "trap", +    [EXCP_FPE] = "floating point", +    [EXCP_DDBS] = "debug data break store", +    [EXCP_DWATCH] = "data watchpoint", +    [EXCP_LTLBL] = "TLB modify", +    [EXCP_TLBL] = "TLB load", +    [EXCP_TLBS] = "TLB store", +    [EXCP_DBE] = "data bus error", +    [EXCP_DDBL] = "debug data break load", +    [EXCP_THREAD] = "thread", +    [EXCP_MDMX] = "MDMX", +    [EXCP_C2E] = "precise coprocessor 2", +    [EXCP_CACHE] = "cache error", +    [EXCP_TLBXI] = "TLB execute-inhibit", +    [EXCP_TLBRI] = "TLB read-inhibit", +    [EXCP_MSADIS] = "MSA disabled", +    [EXCP_MSAFPE] = "MSA floating point", +}; +#endif + +target_ulong exception_resume_pc (CPUMIPSState *env) +{ +    target_ulong bad_pc; +    target_ulong isa_mode; + +    isa_mode = !!(env->hflags & MIPS_HFLAG_M16); +    bad_pc = env->active_tc.PC | isa_mode; +    if (env->hflags & MIPS_HFLAG_BMASK) { +        /* If the exception was raised from a delay slot, come back to +           the jump.  */ +        bad_pc -= (env->hflags & MIPS_HFLAG_B16 ? 2 : 4); +    } + +    return bad_pc; +} + +#if !defined(CONFIG_USER_ONLY) +static void set_hflags_for_handler (CPUMIPSState *env) +{ +    /* Exception handlers are entered in 32-bit mode.  */ +    env->hflags &= ~(MIPS_HFLAG_M16); +    /* ...except that microMIPS lets you choose.  */ +    if (env->insn_flags & ASE_MICROMIPS) { +        env->hflags |= (!!(env->CP0_Config3 +                           & (1 << CP0C3_ISA_ON_EXC)) +                        << MIPS_HFLAG_M16_SHIFT); +    } +} + +static inline void set_badinstr_registers(CPUMIPSState *env) +{ +    if (env->hflags & MIPS_HFLAG_M16) { +        /* TODO: add BadInstr support for microMIPS */ +        return; +    } +    if (env->CP0_Config3 & (1 << CP0C3_BI)) { +        env->CP0_BadInstr = cpu_ldl_code(env, env->active_tc.PC); +    } +    if ((env->CP0_Config3 & (1 << CP0C3_BP)) && +        (env->hflags & MIPS_HFLAG_BMASK)) { +        env->CP0_BadInstrP = cpu_ldl_code(env, env->active_tc.PC - 4); +    } +} +#endif + +void mips_cpu_do_interrupt(CPUState *cs) +{ +#if !defined(CONFIG_USER_ONLY) +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    bool update_badinstr = 0; +    target_ulong offset; +    int cause = -1; +    const char *name; + +    if (qemu_log_enabled() && cs->exception_index != EXCP_EXT_INTERRUPT) { +        if (cs->exception_index < 0 || cs->exception_index > EXCP_LAST) { +            name = "unknown"; +        } else { +            name = excp_names[cs->exception_index]; +        } + +        qemu_log("%s enter: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " %s exception\n", +                 __func__, env->active_tc.PC, env->CP0_EPC, name); +    } +    if (cs->exception_index == EXCP_EXT_INTERRUPT && +        (env->hflags & MIPS_HFLAG_DM)) { +        cs->exception_index = EXCP_DINT; +    } +    offset = 0x180; +    switch (cs->exception_index) { +    case EXCP_DSS: +        env->CP0_Debug |= 1 << CP0DB_DSS; +        /* Debug single step cannot be raised inside a delay slot and +           resume will always occur on the next instruction +           (but we assume the pc has always been updated during +           code translation). */ +        env->CP0_DEPC = env->active_tc.PC | !!(env->hflags & MIPS_HFLAG_M16); +        goto enter_debug_mode; +    case EXCP_DINT: +        env->CP0_Debug |= 1 << CP0DB_DINT; +        goto set_DEPC; +    case EXCP_DIB: +        env->CP0_Debug |= 1 << CP0DB_DIB; +        goto set_DEPC; +    case EXCP_DBp: +        env->CP0_Debug |= 1 << CP0DB_DBp; +        goto set_DEPC; +    case EXCP_DDBS: +        env->CP0_Debug |= 1 << CP0DB_DDBS; +        goto set_DEPC; +    case EXCP_DDBL: +        env->CP0_Debug |= 1 << CP0DB_DDBL; +    set_DEPC: +        env->CP0_DEPC = exception_resume_pc(env); +        env->hflags &= ~MIPS_HFLAG_BMASK; + enter_debug_mode: +        if (env->insn_flags & ISA_MIPS3) { +            env->hflags |= MIPS_HFLAG_64; +        } +        env->hflags |= MIPS_HFLAG_DM | MIPS_HFLAG_CP0; +        env->hflags &= ~(MIPS_HFLAG_KSU); +        /* EJTAG probe trap enable is not implemented... */ +        if (!(env->CP0_Status & (1 << CP0St_EXL))) +            env->CP0_Cause &= ~(1U << CP0Ca_BD); +        env->active_tc.PC = (int32_t)0xBFC00480; +        set_hflags_for_handler(env); +        break; +    case EXCP_RESET: +        cpu_reset(CPU(cpu)); +        break; +    case EXCP_SRESET: +        env->CP0_Status |= (1 << CP0St_SR); +        memset(env->CP0_WatchLo, 0, sizeof(*env->CP0_WatchLo)); +        goto set_error_EPC; +    case EXCP_NMI: +        env->CP0_Status |= (1 << CP0St_NMI); + set_error_EPC: +        env->CP0_ErrorEPC = exception_resume_pc(env); +        env->hflags &= ~MIPS_HFLAG_BMASK; +        env->CP0_Status |= (1 << CP0St_ERL) | (1 << CP0St_BEV); +        if (env->insn_flags & ISA_MIPS3) { +            env->hflags |= MIPS_HFLAG_64; +        } +        env->hflags |= MIPS_HFLAG_CP0; +        env->hflags &= ~(MIPS_HFLAG_KSU); +        if (!(env->CP0_Status & (1 << CP0St_EXL))) +            env->CP0_Cause &= ~(1U << CP0Ca_BD); +        env->active_tc.PC = (int32_t)0xBFC00000; +        set_hflags_for_handler(env); +        break; +    case EXCP_EXT_INTERRUPT: +        cause = 0; +        if (env->CP0_Cause & (1 << CP0Ca_IV)) { +            uint32_t spacing = (env->CP0_IntCtl >> CP0IntCtl_VS) & 0x1f; + +            if ((env->CP0_Status & (1 << CP0St_BEV)) || spacing == 0) { +                offset = 0x200; +            } else { +                uint32_t vector = 0; +                uint32_t pending = (env->CP0_Cause & CP0Ca_IP_mask) >> CP0Ca_IP; + +                if (env->CP0_Config3 & (1 << CP0C3_VEIC)) { +                    /* For VEIC mode, the external interrupt controller feeds +                     * the vector through the CP0Cause IP lines.  */ +                    vector = pending; +                } else { +                    /* Vectored Interrupts +                     * Mask with Status.IM7-IM0 to get enabled interrupts. */ +                    pending &= (env->CP0_Status >> CP0St_IM) & 0xff; +                    /* Find the highest-priority interrupt. */ +                    while (pending >>= 1) { +                        vector++; +                    } +                } +                offset = 0x200 + (vector * (spacing << 5)); +            } +        } +        goto set_EPC; +    case EXCP_LTLBL: +        cause = 1; +        update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); +        goto set_EPC; +    case EXCP_TLBL: +        cause = 2; +        update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); +        if ((env->error_code & EXCP_TLB_NOMATCH) && +            !(env->CP0_Status & (1 << CP0St_EXL))) { +#if defined(TARGET_MIPS64) +            int R = env->CP0_BadVAddr >> 62; +            int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; +            int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; +            int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; + +            if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) && +                (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) +                offset = 0x080; +            else +#endif +                offset = 0x000; +        } +        goto set_EPC; +    case EXCP_TLBS: +        cause = 3; +        update_badinstr = 1; +        if ((env->error_code & EXCP_TLB_NOMATCH) && +            !(env->CP0_Status & (1 << CP0St_EXL))) { +#if defined(TARGET_MIPS64) +            int R = env->CP0_BadVAddr >> 62; +            int UX = (env->CP0_Status & (1 << CP0St_UX)) != 0; +            int SX = (env->CP0_Status & (1 << CP0St_SX)) != 0; +            int KX = (env->CP0_Status & (1 << CP0St_KX)) != 0; + +            if (((R == 0 && UX) || (R == 1 && SX) || (R == 3 && KX)) && +                (!(env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)))) +                offset = 0x080; +            else +#endif +                offset = 0x000; +        } +        goto set_EPC; +    case EXCP_AdEL: +        cause = 4; +        update_badinstr = !(env->error_code & EXCP_INST_NOTAVAIL); +        goto set_EPC; +    case EXCP_AdES: +        cause = 5; +        update_badinstr = 1; +        goto set_EPC; +    case EXCP_IBE: +        cause = 6; +        goto set_EPC; +    case EXCP_DBE: +        cause = 7; +        goto set_EPC; +    case EXCP_SYSCALL: +        cause = 8; +        update_badinstr = 1; +        goto set_EPC; +    case EXCP_BREAK: +        cause = 9; +        update_badinstr = 1; +        goto set_EPC; +    case EXCP_RI: +        cause = 10; +        update_badinstr = 1; +        goto set_EPC; +    case EXCP_CpU: +        cause = 11; +        update_badinstr = 1; +        env->CP0_Cause = (env->CP0_Cause & ~(0x3 << CP0Ca_CE)) | +                         (env->error_code << CP0Ca_CE); +        goto set_EPC; +    case EXCP_OVERFLOW: +        cause = 12; +        update_badinstr = 1; +        goto set_EPC; +    case EXCP_TRAP: +        cause = 13; +        update_badinstr = 1; +        goto set_EPC; +    case EXCP_MSAFPE: +        cause = 14; +        update_badinstr = 1; +        goto set_EPC; +    case EXCP_FPE: +        cause = 15; +        update_badinstr = 1; +        goto set_EPC; +    case EXCP_C2E: +        cause = 18; +        goto set_EPC; +    case EXCP_TLBRI: +        cause = 19; +        update_badinstr = 1; +        goto set_EPC; +    case EXCP_TLBXI: +        cause = 20; +        goto set_EPC; +    case EXCP_MSADIS: +        cause = 21; +        update_badinstr = 1; +        goto set_EPC; +    case EXCP_MDMX: +        cause = 22; +        goto set_EPC; +    case EXCP_DWATCH: +        cause = 23; +        /* XXX: TODO: manage defered watch exceptions */ +        goto set_EPC; +    case EXCP_MCHECK: +        cause = 24; +        goto set_EPC; +    case EXCP_THREAD: +        cause = 25; +        goto set_EPC; +    case EXCP_DSPDIS: +        cause = 26; +        goto set_EPC; +    case EXCP_CACHE: +        cause = 30; +        if (env->CP0_Status & (1 << CP0St_BEV)) { +            offset = 0x100; +        } else { +            offset = 0x20000100; +        } + set_EPC: +        if (!(env->CP0_Status & (1 << CP0St_EXL))) { +            env->CP0_EPC = exception_resume_pc(env); +            if (update_badinstr) { +                set_badinstr_registers(env); +            } +            if (env->hflags & MIPS_HFLAG_BMASK) { +                env->CP0_Cause |= (1U << CP0Ca_BD); +            } else { +                env->CP0_Cause &= ~(1U << CP0Ca_BD); +            } +            env->CP0_Status |= (1 << CP0St_EXL); +            if (env->insn_flags & ISA_MIPS3) { +                env->hflags |= MIPS_HFLAG_64; +            } +            env->hflags |= MIPS_HFLAG_CP0; +            env->hflags &= ~(MIPS_HFLAG_KSU); +        } +        env->hflags &= ~MIPS_HFLAG_BMASK; +        if (env->CP0_Status & (1 << CP0St_BEV)) { +            env->active_tc.PC = (int32_t)0xBFC00200; +        } else { +            env->active_tc.PC = (int32_t)(env->CP0_EBase & ~0x3ff); +        } +        env->active_tc.PC += offset; +        set_hflags_for_handler(env); +        env->CP0_Cause = (env->CP0_Cause & ~(0x1f << CP0Ca_EC)) | (cause << CP0Ca_EC); +        break; +    default: +        qemu_log("Invalid MIPS exception %d. Exiting\n", cs->exception_index); +        printf("Invalid MIPS exception %d. Exiting\n", cs->exception_index); +        exit(1); +    } +    if (qemu_log_enabled() && cs->exception_index != EXCP_EXT_INTERRUPT) { +        qemu_log("%s: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx " cause %d\n" +                "    S %08x C %08x A " TARGET_FMT_lx " D " TARGET_FMT_lx "\n", +                __func__, env->active_tc.PC, env->CP0_EPC, cause, +                env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr, +                env->CP0_DEPC); +    } +#endif +    cs->exception_index = EXCP_NONE; +} + +bool mips_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ +    if (interrupt_request & CPU_INTERRUPT_HARD) { +        MIPSCPU *cpu = MIPS_CPU(cs); +        CPUMIPSState *env = &cpu->env; + +        if (cpu_mips_hw_interrupts_pending(env)) { +            /* Raise it */ +            cs->exception_index = EXCP_EXT_INTERRUPT; +            env->error_code = 0; +            mips_cpu_do_interrupt(cs); +            return true; +        } +    } +    return false; +} + +#if !defined(CONFIG_USER_ONLY) +void r4k_invalidate_tlb (CPUMIPSState *env, int idx, int use_extra) +{ +    MIPSCPU *cpu = mips_env_get_cpu(env); +    CPUState *cs; +    r4k_tlb_t *tlb; +    target_ulong addr; +    target_ulong end; +    uint8_t ASID = env->CP0_EntryHi & 0xFF; +    target_ulong mask; + +    tlb = &env->tlb->mmu.r4k.tlb[idx]; +    /* The qemu TLB is flushed when the ASID changes, so no need to +       flush these entries again.  */ +    if (tlb->G == 0 && tlb->ASID != ASID) { +        return; +    } + +    if (use_extra && env->tlb->tlb_in_use < MIPS_TLB_MAX) { +        /* For tlbwr, we can shadow the discarded entry into +           a new (fake) TLB entry, as long as the guest can not +           tell that it's there.  */ +        env->tlb->mmu.r4k.tlb[env->tlb->tlb_in_use] = *tlb; +        env->tlb->tlb_in_use++; +        return; +    } + +    /* 1k pages are not supported. */ +    mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); +    if (tlb->V0) { +        cs = CPU(cpu); +        addr = tlb->VPN & ~mask; +#if defined(TARGET_MIPS64) +        if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { +            addr |= 0x3FFFFF0000000000ULL; +        } +#endif +        end = addr | (mask >> 1); +        while (addr < end) { +            tlb_flush_page(cs, addr); +            addr += TARGET_PAGE_SIZE; +        } +    } +    if (tlb->V1) { +        cs = CPU(cpu); +        addr = (tlb->VPN & ~mask) | ((mask >> 1) + 1); +#if defined(TARGET_MIPS64) +        if (addr >= (0xFFFFFFFF80000000ULL & env->SEGMask)) { +            addr |= 0x3FFFFF0000000000ULL; +        } +#endif +        end = addr | mask; +        while (addr - 1 < end) { +            tlb_flush_page(cs, addr); +            addr += TARGET_PAGE_SIZE; +        } +    } +} +#endif diff --git a/target-mips/helper.h b/target-mips/helper.h new file mode 100644 index 00000000..2b28e875 --- /dev/null +++ b/target-mips/helper.h @@ -0,0 +1,944 @@ +DEF_HELPER_3(raise_exception_err, noreturn, env, i32, int) +DEF_HELPER_2(raise_exception, noreturn, env, i32) + +DEF_HELPER_1(do_semihosting, void, env) + +#ifdef TARGET_MIPS64 +DEF_HELPER_4(sdl, void, env, tl, tl, int) +DEF_HELPER_4(sdr, void, env, tl, tl, int) +#endif +DEF_HELPER_4(swl, void, env, tl, tl, int) +DEF_HELPER_4(swr, void, env, tl, tl, int) + +#ifndef CONFIG_USER_ONLY +DEF_HELPER_3(ll, tl, env, tl, int) +DEF_HELPER_4(sc, tl, env, tl, tl, int) +#ifdef TARGET_MIPS64 +DEF_HELPER_3(lld, tl, env, tl, int) +DEF_HELPER_4(scd, tl, env, tl, tl, int) +#endif +#endif + +DEF_HELPER_FLAGS_1(clo, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, tl, tl) +#ifdef TARGET_MIPS64 +DEF_HELPER_FLAGS_1(dclo, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(dclz, TCG_CALL_NO_RWG_SE, tl, tl) +#endif + +DEF_HELPER_3(muls, tl, env, tl, tl) +DEF_HELPER_3(mulsu, tl, env, tl, tl) +DEF_HELPER_3(macc, tl, env, tl, tl) +DEF_HELPER_3(maccu, tl, env, tl, tl) +DEF_HELPER_3(msac, tl, env, tl, tl) +DEF_HELPER_3(msacu, tl, env, tl, tl) +DEF_HELPER_3(mulhi, tl, env, tl, tl) +DEF_HELPER_3(mulhiu, tl, env, tl, tl) +DEF_HELPER_3(mulshi, tl, env, tl, tl) +DEF_HELPER_3(mulshiu, tl, env, tl, tl) +DEF_HELPER_3(macchi, tl, env, tl, tl) +DEF_HELPER_3(macchiu, tl, env, tl, tl) +DEF_HELPER_3(msachi, tl, env, tl, tl) +DEF_HELPER_3(msachiu, tl, env, tl, tl) + +DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) +#ifdef TARGET_MIPS64 +DEF_HELPER_FLAGS_1(dbitswap, TCG_CALL_NO_RWG_SE, tl, tl) +#endif + +#ifndef CONFIG_USER_ONLY +/* CP0 helpers */ +DEF_HELPER_1(mfc0_mvpcontrol, tl, env) +DEF_HELPER_1(mfc0_mvpconf0, tl, env) +DEF_HELPER_1(mfc0_mvpconf1, tl, env) +DEF_HELPER_1(mftc0_vpecontrol, tl, env) +DEF_HELPER_1(mftc0_vpeconf0, tl, env) +DEF_HELPER_1(mfc0_random, tl, env) +DEF_HELPER_1(mfc0_tcstatus, tl, env) +DEF_HELPER_1(mftc0_tcstatus, tl, env) +DEF_HELPER_1(mfc0_tcbind, tl, env) +DEF_HELPER_1(mftc0_tcbind, tl, env) +DEF_HELPER_1(mfc0_tcrestart, tl, env) +DEF_HELPER_1(mftc0_tcrestart, tl, env) +DEF_HELPER_1(mfc0_tchalt, tl, env) +DEF_HELPER_1(mftc0_tchalt, tl, env) +DEF_HELPER_1(mfc0_tccontext, tl, env) +DEF_HELPER_1(mftc0_tccontext, tl, env) +DEF_HELPER_1(mfc0_tcschedule, tl, env) +DEF_HELPER_1(mftc0_tcschedule, tl, env) +DEF_HELPER_1(mfc0_tcschefback, tl, env) +DEF_HELPER_1(mftc0_tcschefback, tl, env) +DEF_HELPER_1(mfc0_count, tl, env) +DEF_HELPER_1(mftc0_entryhi, tl, env) +DEF_HELPER_1(mftc0_status, tl, env) +DEF_HELPER_1(mftc0_cause, tl, env) +DEF_HELPER_1(mftc0_epc, tl, env) +DEF_HELPER_1(mftc0_ebase, tl, env) +DEF_HELPER_2(mftc0_configx, tl, env, tl) +DEF_HELPER_1(mfc0_lladdr, tl, env) +DEF_HELPER_2(mfc0_watchlo, tl, env, i32) +DEF_HELPER_2(mfc0_watchhi, tl, env, i32) +DEF_HELPER_1(mfc0_debug, tl, env) +DEF_HELPER_1(mftc0_debug, tl, env) +#ifdef TARGET_MIPS64 +DEF_HELPER_1(dmfc0_tcrestart, tl, env) +DEF_HELPER_1(dmfc0_tchalt, tl, env) +DEF_HELPER_1(dmfc0_tccontext, tl, env) +DEF_HELPER_1(dmfc0_tcschedule, tl, env) +DEF_HELPER_1(dmfc0_tcschefback, tl, env) +DEF_HELPER_1(dmfc0_lladdr, tl, env) +DEF_HELPER_2(dmfc0_watchlo, tl, env, i32) +#endif /* TARGET_MIPS64 */ + +DEF_HELPER_2(mtc0_index, void, env, tl) +DEF_HELPER_2(mtc0_mvpcontrol, void, env, tl) +DEF_HELPER_2(mtc0_vpecontrol, void, env, tl) +DEF_HELPER_2(mttc0_vpecontrol, void, env, tl) +DEF_HELPER_2(mtc0_vpeconf0, void, env, tl) +DEF_HELPER_2(mttc0_vpeconf0, void, env, tl) +DEF_HELPER_2(mtc0_vpeconf1, void, env, tl) +DEF_HELPER_2(mtc0_yqmask, void, env, tl) +DEF_HELPER_2(mtc0_vpeopt, void, env, tl) +DEF_HELPER_2(mtc0_entrylo0, void, env, tl) +DEF_HELPER_2(mtc0_tcstatus, void, env, tl) +DEF_HELPER_2(mttc0_tcstatus, void, env, tl) +DEF_HELPER_2(mtc0_tcbind, void, env, tl) +DEF_HELPER_2(mttc0_tcbind, void, env, tl) +DEF_HELPER_2(mtc0_tcrestart, void, env, tl) +DEF_HELPER_2(mttc0_tcrestart, void, env, tl) +DEF_HELPER_2(mtc0_tchalt, void, env, tl) +DEF_HELPER_2(mttc0_tchalt, void, env, tl) +DEF_HELPER_2(mtc0_tccontext, void, env, tl) +DEF_HELPER_2(mttc0_tccontext, void, env, tl) +DEF_HELPER_2(mtc0_tcschedule, void, env, tl) +DEF_HELPER_2(mttc0_tcschedule, void, env, tl) +DEF_HELPER_2(mtc0_tcschefback, void, env, tl) +DEF_HELPER_2(mttc0_tcschefback, void, env, tl) +DEF_HELPER_2(mtc0_entrylo1, void, env, tl) +DEF_HELPER_2(mtc0_context, void, env, tl) +DEF_HELPER_2(mtc0_pagemask, void, env, tl) +DEF_HELPER_2(mtc0_pagegrain, void, env, tl) +DEF_HELPER_2(mtc0_wired, void, env, tl) +DEF_HELPER_2(mtc0_srsconf0, void, env, tl) +DEF_HELPER_2(mtc0_srsconf1, void, env, tl) +DEF_HELPER_2(mtc0_srsconf2, void, env, tl) +DEF_HELPER_2(mtc0_srsconf3, void, env, tl) +DEF_HELPER_2(mtc0_srsconf4, void, env, tl) +DEF_HELPER_2(mtc0_hwrena, void, env, tl) +DEF_HELPER_2(mtc0_count, void, env, tl) +DEF_HELPER_2(mtc0_entryhi, void, env, tl) +DEF_HELPER_2(mttc0_entryhi, void, env, tl) +DEF_HELPER_2(mtc0_compare, void, env, tl) +DEF_HELPER_2(mtc0_status, void, env, tl) +DEF_HELPER_2(mttc0_status, void, env, tl) +DEF_HELPER_2(mtc0_intctl, void, env, tl) +DEF_HELPER_2(mtc0_srsctl, void, env, tl) +DEF_HELPER_2(mtc0_cause, void, env, tl) +DEF_HELPER_2(mttc0_cause, void, env, tl) +DEF_HELPER_2(mtc0_ebase, void, env, tl) +DEF_HELPER_2(mttc0_ebase, void, env, tl) +DEF_HELPER_2(mtc0_config0, void, env, tl) +DEF_HELPER_2(mtc0_config2, void, env, tl) +DEF_HELPER_2(mtc0_config3, void, env, tl) +DEF_HELPER_2(mtc0_config4, void, env, tl) +DEF_HELPER_2(mtc0_config5, void, env, tl) +DEF_HELPER_2(mtc0_lladdr, void, env, tl) +DEF_HELPER_3(mtc0_watchlo, void, env, tl, i32) +DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32) +DEF_HELPER_2(mtc0_xcontext, void, env, tl) +DEF_HELPER_2(mtc0_framemask, void, env, tl) +DEF_HELPER_2(mtc0_debug, void, env, tl) +DEF_HELPER_2(mttc0_debug, void, env, tl) +DEF_HELPER_2(mtc0_performance0, void, env, tl) +DEF_HELPER_2(mtc0_taglo, void, env, tl) +DEF_HELPER_2(mtc0_datalo, void, env, tl) +DEF_HELPER_2(mtc0_taghi, void, env, tl) +DEF_HELPER_2(mtc0_datahi, void, env, tl) + +#if defined(TARGET_MIPS64) +DEF_HELPER_2(dmtc0_entrylo0, void, env, i64) +DEF_HELPER_2(dmtc0_entrylo1, void, env, i64) +#endif + +/* MIPS MT functions */ +DEF_HELPER_2(mftgpr, tl, env, i32) +DEF_HELPER_2(mftlo, tl, env, i32) +DEF_HELPER_2(mfthi, tl, env, i32) +DEF_HELPER_2(mftacx, tl, env, i32) +DEF_HELPER_1(mftdsp, tl, env) +DEF_HELPER_3(mttgpr, void, env, tl, i32) +DEF_HELPER_3(mttlo, void, env, tl, i32) +DEF_HELPER_3(mtthi, void, env, tl, i32) +DEF_HELPER_3(mttacx, void, env, tl, i32) +DEF_HELPER_2(mttdsp, void, env, tl) +DEF_HELPER_0(dmt, tl) +DEF_HELPER_0(emt, tl) +DEF_HELPER_1(dvpe, tl, env) +DEF_HELPER_1(evpe, tl, env) +#endif /* !CONFIG_USER_ONLY */ + +/* microMIPS functions */ +DEF_HELPER_4(lwm, void, env, tl, tl, i32) +DEF_HELPER_4(swm, void, env, tl, tl, i32) +#ifdef TARGET_MIPS64 +DEF_HELPER_4(ldm, void, env, tl, tl, i32) +DEF_HELPER_4(sdm, void, env, tl, tl, i32) +#endif + +DEF_HELPER_2(fork, void, tl, tl) +DEF_HELPER_2(yield, tl, env, tl) + +/* CP1 functions */ +DEF_HELPER_2(cfc1, tl, env, i32) +DEF_HELPER_4(ctc1, void, env, tl, i32, i32) + +DEF_HELPER_2(float_cvtd_s, i64, env, i32) +DEF_HELPER_2(float_cvtd_w, i64, env, i32) +DEF_HELPER_2(float_cvtd_l, i64, env, i64) +DEF_HELPER_2(float_cvtl_d, i64, env, i64) +DEF_HELPER_2(float_cvtl_s, i64, env, i32) +DEF_HELPER_2(float_cvtps_pw, i64, env, i64) +DEF_HELPER_2(float_cvtpw_ps, i64, env, i64) +DEF_HELPER_2(float_cvts_d, i32, env, i64) +DEF_HELPER_2(float_cvts_w, i32, env, i32) +DEF_HELPER_2(float_cvts_l, i32, env, i64) +DEF_HELPER_2(float_cvts_pl, i32, env, i32) +DEF_HELPER_2(float_cvts_pu, i32, env, i32) +DEF_HELPER_2(float_cvtw_s, i32, env, i32) +DEF_HELPER_2(float_cvtw_d, i32, env, i64) + +DEF_HELPER_3(float_addr_ps, i64, env, i64, i64) +DEF_HELPER_3(float_mulr_ps, i64, env, i64, i64) + +DEF_HELPER_FLAGS_1(float_class_s, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_1(float_class_d, TCG_CALL_NO_RWG_SE, i64, i64) + +#define FOP_PROTO(op)                                     \ +DEF_HELPER_4(float_ ## op ## _s, i32, env, i32, i32, i32) \ +DEF_HELPER_4(float_ ## op ## _d, i64, env, i64, i64, i64) +FOP_PROTO(maddf) +FOP_PROTO(msubf) +#undef FOP_PROTO + +#define FOP_PROTO(op)                                \ +DEF_HELPER_3(float_ ## op ## _s, i32, env, i32, i32) \ +DEF_HELPER_3(float_ ## op ## _d, i64, env, i64, i64) +FOP_PROTO(max) +FOP_PROTO(maxa) +FOP_PROTO(min) +FOP_PROTO(mina) +#undef FOP_PROTO + +#define FOP_PROTO(op)                            \ +DEF_HELPER_2(float_ ## op ## l_s, i64, env, i32) \ +DEF_HELPER_2(float_ ## op ## l_d, i64, env, i64) \ +DEF_HELPER_2(float_ ## op ## w_s, i32, env, i32) \ +DEF_HELPER_2(float_ ## op ## w_d, i32, env, i64) +FOP_PROTO(round) +FOP_PROTO(trunc) +FOP_PROTO(ceil) +FOP_PROTO(floor) +#undef FOP_PROTO + +#define FOP_PROTO(op)                            \ +DEF_HELPER_2(float_ ## op ## _s, i32, env, i32)  \ +DEF_HELPER_2(float_ ## op ## _d, i64, env, i64) +FOP_PROTO(sqrt) +FOP_PROTO(rsqrt) +FOP_PROTO(recip) +FOP_PROTO(rint) +#undef FOP_PROTO + +#define FOP_PROTO(op)                       \ +DEF_HELPER_1(float_ ## op ## _s, i32, i32)  \ +DEF_HELPER_1(float_ ## op ## _d, i64, i64)  \ +DEF_HELPER_1(float_ ## op ## _ps, i64, i64) +FOP_PROTO(abs) +FOP_PROTO(chs) +#undef FOP_PROTO + +#define FOP_PROTO(op)                            \ +DEF_HELPER_2(float_ ## op ## _s, i32, env, i32)  \ +DEF_HELPER_2(float_ ## op ## _d, i64, env, i64)  \ +DEF_HELPER_2(float_ ## op ## _ps, i64, env, i64) +FOP_PROTO(recip1) +FOP_PROTO(rsqrt1) +#undef FOP_PROTO + +#define FOP_PROTO(op)                                  \ +DEF_HELPER_3(float_ ## op ## _s, i32, env, i32, i32)   \ +DEF_HELPER_3(float_ ## op ## _d, i64, env, i64, i64)   \ +DEF_HELPER_3(float_ ## op ## _ps, i64, env, i64, i64) +FOP_PROTO(add) +FOP_PROTO(sub) +FOP_PROTO(mul) +FOP_PROTO(div) +FOP_PROTO(recip2) +FOP_PROTO(rsqrt2) +#undef FOP_PROTO + +#define FOP_PROTO(op)                                      \ +DEF_HELPER_4(float_ ## op ## _s, i32, env, i32, i32, i32)  \ +DEF_HELPER_4(float_ ## op ## _d, i64, env, i64, i64, i64)  \ +DEF_HELPER_4(float_ ## op ## _ps, i64, env, i64, i64, i64) +FOP_PROTO(madd) +FOP_PROTO(msub) +FOP_PROTO(nmadd) +FOP_PROTO(nmsub) +#undef FOP_PROTO + +#define FOP_PROTO(op)                                    \ +DEF_HELPER_4(cmp_d_ ## op, void, env, i64, i64, int)     \ +DEF_HELPER_4(cmpabs_d_ ## op, void, env, i64, i64, int)  \ +DEF_HELPER_4(cmp_s_ ## op, void, env, i32, i32, int)     \ +DEF_HELPER_4(cmpabs_s_ ## op, void, env, i32, i32, int)  \ +DEF_HELPER_4(cmp_ps_ ## op, void, env, i64, i64, int)    \ +DEF_HELPER_4(cmpabs_ps_ ## op, void, env, i64, i64, int) +FOP_PROTO(f) +FOP_PROTO(un) +FOP_PROTO(eq) +FOP_PROTO(ueq) +FOP_PROTO(olt) +FOP_PROTO(ult) +FOP_PROTO(ole) +FOP_PROTO(ule) +FOP_PROTO(sf) +FOP_PROTO(ngle) +FOP_PROTO(seq) +FOP_PROTO(ngl) +FOP_PROTO(lt) +FOP_PROTO(nge) +FOP_PROTO(le) +FOP_PROTO(ngt) +#undef FOP_PROTO + +#define FOP_PROTO(op) \ +DEF_HELPER_3(r6_cmp_d_ ## op, i64, env, i64, i64) \ +DEF_HELPER_3(r6_cmp_s_ ## op, i32, env, i32, i32) +FOP_PROTO(af) +FOP_PROTO(un) +FOP_PROTO(eq) +FOP_PROTO(ueq) +FOP_PROTO(lt) +FOP_PROTO(ult) +FOP_PROTO(le) +FOP_PROTO(ule) +FOP_PROTO(saf) +FOP_PROTO(sun) +FOP_PROTO(seq) +FOP_PROTO(sueq) +FOP_PROTO(slt) +FOP_PROTO(sult) +FOP_PROTO(sle) +FOP_PROTO(sule) +FOP_PROTO(or) +FOP_PROTO(une) +FOP_PROTO(ne) +FOP_PROTO(sor) +FOP_PROTO(sune) +FOP_PROTO(sne) +#undef FOP_PROTO + +/* Special functions */ +#ifndef CONFIG_USER_ONLY +DEF_HELPER_1(tlbwi, void, env) +DEF_HELPER_1(tlbwr, void, env) +DEF_HELPER_1(tlbp, void, env) +DEF_HELPER_1(tlbr, void, env) +DEF_HELPER_1(tlbinv, void, env) +DEF_HELPER_1(tlbinvf, void, env) +DEF_HELPER_1(di, tl, env) +DEF_HELPER_1(ei, tl, env) +DEF_HELPER_1(eret, void, env) +DEF_HELPER_1(eretnc, void, env) +DEF_HELPER_1(deret, void, env) +#endif /* !CONFIG_USER_ONLY */ +DEF_HELPER_1(rdhwr_cpunum, tl, env) +DEF_HELPER_1(rdhwr_synci_step, tl, env) +DEF_HELPER_1(rdhwr_cc, tl, env) +DEF_HELPER_1(rdhwr_ccres, tl, env) +DEF_HELPER_2(pmon, void, env, int) +DEF_HELPER_1(wait, void, env) + +/* Loongson multimedia functions.  */ +DEF_HELPER_FLAGS_2(paddsh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(paddush, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(paddh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(paddw, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(paddsb, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(paddusb, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(paddb, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(psubsh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psubush, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psubh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psubw, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psubsb, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psubusb, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psubb, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(pshufh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(packsswh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(packsshb, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(packushb, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(punpcklhw, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(punpckhhw, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(punpcklbh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(punpckhbh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(punpcklwd, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(punpckhwd, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(pavgh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pavgb, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pmaxsh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pminsh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pmaxub, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pminub, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(pcmpeqw, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pcmpgtw, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pcmpeqh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pcmpgth, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pcmpeqb, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pcmpgtb, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(psllw, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psllh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psrlw, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psrlh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psraw, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(psrah, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(pmullh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pmulhh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pmulhuh, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(pmaddhw, TCG_CALL_NO_RWG_SE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(pasubub, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_1(biadd, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(pmovmskb, TCG_CALL_NO_RWG_SE, i64, i64) + +/*** MIPS DSP ***/ +/* DSP Arithmetic Sub-class insns */ +DEF_HELPER_FLAGS_3(addq_ph, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(addq_s_ph, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(addq_qh, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(addq_s_qh, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(addq_s_w, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(addq_pw, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(addq_s_pw, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(addu_qb, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(addu_s_qb, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_2(adduh_qb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(adduh_r_qb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_3(addu_ph, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(addu_s_ph, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_2(addqh_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(addqh_r_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(addqh_w, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(addqh_r_w, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(addu_ob, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(addu_s_ob, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_2(adduh_ob, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(adduh_r_ob, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_3(addu_qh, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(addu_s_qh, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(subq_ph, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(subq_s_ph, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(subq_qh, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(subq_s_qh, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(subq_s_w, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(subq_pw, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(subq_s_pw, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(subu_qb, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(subu_s_qb, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_2(subuh_qb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(subuh_r_qb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_3(subu_ph, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(subu_s_ph, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_2(subqh_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(subqh_r_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(subqh_w, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(subqh_r_w, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(subu_ob, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(subu_s_ob, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_2(subuh_ob, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(subuh_r_ob, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_3(subu_qh, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(subu_s_qh, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(addsc, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(addwc, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_2(modsub, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_1(raddu_w_qb, TCG_CALL_NO_RWG_SE, tl, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_1(raddu_l_ob, TCG_CALL_NO_RWG_SE, tl, tl) +#endif +DEF_HELPER_FLAGS_2(absq_s_qb, 0, tl, tl, env) +DEF_HELPER_FLAGS_2(absq_s_ph, 0, tl, tl, env) +DEF_HELPER_FLAGS_2(absq_s_w, 0, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_2(absq_s_ob, 0, tl, tl, env) +DEF_HELPER_FLAGS_2(absq_s_qh, 0, tl, tl, env) +DEF_HELPER_FLAGS_2(absq_s_pw, 0, tl, tl, env) +#endif +DEF_HELPER_FLAGS_2(precr_qb_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(precrq_qb_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_3(precr_sra_ph_w, TCG_CALL_NO_RWG_SE, +                   tl, i32, tl, tl) +DEF_HELPER_FLAGS_3(precr_sra_r_ph_w, TCG_CALL_NO_RWG_SE, +                   tl, i32, tl, tl) +DEF_HELPER_FLAGS_2(precrq_ph_w, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_3(precrq_rs_ph_w, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_2(precr_ob_qh, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_3(precr_sra_qh_pw, +                   TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) +DEF_HELPER_FLAGS_3(precr_sra_r_qh_pw, +                   TCG_CALL_NO_RWG_SE, tl, tl, tl, i32) +DEF_HELPER_FLAGS_2(precrq_ob_qh, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(precrq_qh_pw, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_3(precrq_rs_qh_pw, +                   TCG_CALL_NO_RWG_SE, tl, tl, tl, env) +DEF_HELPER_FLAGS_2(precrq_pw_l, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#endif +DEF_HELPER_FLAGS_3(precrqu_s_qb_ph, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(precrqu_s_ob_qh, +                   TCG_CALL_NO_RWG_SE, tl, tl, tl, env) + +DEF_HELPER_FLAGS_1(preceq_pw_qhl, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(preceq_pw_qhr, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(preceq_pw_qhla, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(preceq_pw_qhra, TCG_CALL_NO_RWG_SE, tl, tl) +#endif +DEF_HELPER_FLAGS_1(precequ_ph_qbl, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(precequ_ph_qbr, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(precequ_ph_qbla, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(precequ_ph_qbra, TCG_CALL_NO_RWG_SE, tl, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_1(precequ_qh_obl, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(precequ_qh_obr, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(precequ_qh_obla, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(precequ_qh_obra, TCG_CALL_NO_RWG_SE, tl, tl) +#endif +DEF_HELPER_FLAGS_1(preceu_ph_qbl, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(preceu_ph_qbr, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(preceu_ph_qbla, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(preceu_ph_qbra, TCG_CALL_NO_RWG_SE, tl, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_1(preceu_qh_obl, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(preceu_qh_obr, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(preceu_qh_obla, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(preceu_qh_obra, TCG_CALL_NO_RWG_SE, tl, tl) +#endif + +/* DSP GPR-Based Shift Sub-class insns */ +DEF_HELPER_FLAGS_3(shll_qb, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(shll_ob, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(shll_ph, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(shll_s_ph, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(shll_qh, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(shll_s_qh, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(shll_s_w, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(shll_pw, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(shll_s_pw, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_2(shrl_qb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(shrl_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_2(shrl_ob, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(shrl_qh, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#endif +DEF_HELPER_FLAGS_2(shra_qb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(shra_r_qb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_2(shra_ob, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(shra_r_ob, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#endif +DEF_HELPER_FLAGS_2(shra_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(shra_r_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(shra_r_w, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_2(shra_qh, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(shra_r_qh, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(shra_pw, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(shra_r_pw, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#endif + +/* DSP Multiply Sub-class insns */ +DEF_HELPER_FLAGS_3(muleu_s_ph_qbl, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(muleu_s_ph_qbr, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(muleu_s_qh_obl, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(muleu_s_qh_obr, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(mulq_rs_ph, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(mulq_rs_qh, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(muleq_s_w_phl, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(muleq_s_w_phr, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(muleq_s_pw_qhl, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(muleq_s_pw_qhr, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_4(dpau_h_qbl, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(dpau_h_qbr, 0, void, i32, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_4(dpau_h_obl, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(dpau_h_obr, 0, void, tl, tl, i32, env) +#endif +DEF_HELPER_FLAGS_4(dpsu_h_qbl, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(dpsu_h_qbr, 0, void, i32, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_4(dpsu_h_obl, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(dpsu_h_obr, 0, void, tl, tl, i32, env) +#endif +DEF_HELPER_FLAGS_4(dpa_w_ph, 0, void, i32, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_4(dpa_w_qh, 0, void, tl, tl, i32, env) +#endif +DEF_HELPER_FLAGS_4(dpax_w_ph, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(dpaq_s_w_ph, 0, void, i32, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_4(dpaq_s_w_qh, 0, void, tl, tl, i32, env) +#endif +DEF_HELPER_FLAGS_4(dpaqx_s_w_ph, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(dpaqx_sa_w_ph, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(dps_w_ph, 0, void, i32, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_4(dps_w_qh, 0, void, tl, tl, i32, env) +#endif +DEF_HELPER_FLAGS_4(dpsx_w_ph, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(dpsq_s_w_ph, 0, void, i32, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_4(dpsq_s_w_qh, 0, void, tl, tl, i32, env) +#endif +DEF_HELPER_FLAGS_4(dpsqx_s_w_ph, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(dpsqx_sa_w_ph, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(mulsaq_s_w_ph, 0, void, i32, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_4(mulsaq_s_w_qh, 0, void, tl, tl, i32, env) +#endif +DEF_HELPER_FLAGS_4(dpaq_sa_l_w, 0, void, i32, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_4(dpaq_sa_l_pw, 0, void, tl, tl, i32, env) +#endif +DEF_HELPER_FLAGS_4(dpsq_sa_l_w, 0, void, i32, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_4(dpsq_sa_l_pw, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(mulsaq_s_l_pw, 0, void, tl, tl, i32, env) +#endif +DEF_HELPER_FLAGS_4(maq_s_w_phl, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(maq_s_w_phr, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(maq_sa_w_phl, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_4(maq_sa_w_phr, 0, void, i32, tl, tl, env) +DEF_HELPER_FLAGS_3(mul_ph, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(mul_s_ph, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(mulq_s_ph, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(mulq_s_w, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(mulq_rs_w, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_4(mulsa_w_ph, 0, void, i32, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_4(maq_s_w_qhll, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(maq_s_w_qhlr, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(maq_s_w_qhrl, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(maq_s_w_qhrr, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(maq_sa_w_qhll, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(maq_sa_w_qhlr, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(maq_sa_w_qhrl, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(maq_sa_w_qhrr, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(maq_s_l_pwl, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(maq_s_l_pwr, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(dmadd, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(dmaddu, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(dmsub, 0, void, tl, tl, i32, env) +DEF_HELPER_FLAGS_4(dmsubu, 0, void, tl, tl, i32, env) +#endif + +/* DSP Bit/Manipulation Sub-class insns */ +DEF_HELPER_FLAGS_1(bitrev, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_3(insv, 0, tl, env, tl, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(dinsv, 0, tl, env, tl, tl) +#endif + +/* DSP Compare-Pick Sub-class insns */ +DEF_HELPER_FLAGS_3(cmpu_eq_qb, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmpu_lt_qb, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmpu_le_qb, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_2(cmpgu_eq_qb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(cmpgu_lt_qb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(cmpgu_le_qb, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_3(cmp_eq_ph, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmp_lt_ph, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmp_le_ph, 0, void, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(cmpu_eq_ob, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmpu_lt_ob, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmpu_le_ob, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmpgdu_eq_ob, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(cmpgdu_lt_ob, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(cmpgdu_le_ob, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_2(cmpgu_eq_ob, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(cmpgu_lt_ob, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_2(cmpgu_le_ob, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_FLAGS_3(cmp_eq_qh, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmp_lt_qh, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmp_le_qh, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmp_eq_pw, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmp_lt_pw, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_3(cmp_le_pw, 0, void, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(pick_qb, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(pick_ph, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(pick_ob, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(pick_qh, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(pick_pw, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_2(packrl_ph, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_2(packrl_pw, TCG_CALL_NO_RWG_SE, tl, tl, tl) +#endif + +/* DSP Accumulator and DSPControl Access Sub-class insns */ +DEF_HELPER_FLAGS_3(extr_w, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(extr_r_w, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(extr_rs_w, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(dextr_w, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(dextr_r_w, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(dextr_rs_w, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(dextr_l, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(dextr_r_l, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(dextr_rs_l, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(extr_s_h, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(dextr_s_h, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(extp, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(extpdp, 0, tl, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(dextp, 0, tl, tl, tl, env) +DEF_HELPER_FLAGS_3(dextpdp, 0, tl, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(shilo, 0, void, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(dshilo, 0, void, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(mthlip, 0, void, tl, tl, env) +#if defined(TARGET_MIPS64) +DEF_HELPER_FLAGS_3(dmthlip, 0, void, tl, tl, env) +#endif +DEF_HELPER_FLAGS_3(wrdsp, 0, void, tl, tl, env) +DEF_HELPER_FLAGS_2(rddsp, 0, tl, tl, env) + +/* MIPS SIMD Architecture */ +DEF_HELPER_4(msa_andi_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ori_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_nori_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_xori_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bmnzi_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bmzi_b, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bseli_b, void, env, i32, i32, i32) +DEF_HELPER_5(msa_shf_df, void, env, i32, i32, i32, i32) + +DEF_HELPER_5(msa_addvi_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_subvi_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_maxi_s_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_maxi_u_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_mini_s_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_mini_u_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_ceqi_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_clti_s_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_clti_u_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_clei_s_df, void, env, i32, i32, i32, s32) +DEF_HELPER_5(msa_clei_u_df, void, env, i32, i32, i32, s32) +DEF_HELPER_4(msa_ldi_df, void, env, i32, i32, s32) + +DEF_HELPER_5(msa_slli_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srai_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srli_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bclri_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bseti_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bnegi_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_binsli_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_binsri_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_sat_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_sat_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srari_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srlri_df, void, env, i32, i32, i32, i32) + +DEF_HELPER_5(msa_sll_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_sra_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srl_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bclr_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bset_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_bneg_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_binsl_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_binsr_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_addv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_subv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_max_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_max_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_min_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_min_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_max_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_min_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ceq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_clt_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_clt_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_cle_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_cle_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_add_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_adds_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_adds_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_adds_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ave_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ave_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_aver_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_aver_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_subs_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_subs_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_subsus_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_subsuu_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_asub_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_asub_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_mulv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_maddv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_msubv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_div_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_div_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_mod_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_mod_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dotp_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dotp_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dpadd_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dpadd_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dpsub_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_dpsub_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_sld_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_splat_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_pckev_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_pckod_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ilvl_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ilvr_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ilvev_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ilvod_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_vshf_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srar_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_srlr_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_hadd_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_hadd_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_hsub_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_hsub_u_df, void, env, i32, i32, i32, i32) + +DEF_HELPER_5(msa_sldi_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_splati_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_copy_s_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_copy_u_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_insert_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_insve_df, void, env, i32, i32, i32, i32) +DEF_HELPER_3(msa_ctcmsa, void, env, tl, i32) +DEF_HELPER_2(msa_cfcmsa, tl, env, i32) +DEF_HELPER_3(msa_move_v, void, env, i32, i32) + +DEF_HELPER_5(msa_fcaf_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcun_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fceq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcueq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fclt_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcult_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcle_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcule_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsaf_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsun_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fseq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsueq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fslt_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsult_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsle_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsule_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fadd_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsub_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmul_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fdiv_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmadd_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmsub_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fexp2_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fexdo_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_ftq_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmin_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmin_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmax_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fmax_a_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcor_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcune_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fcne_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_mul_q_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_madd_q_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_msub_q_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsor_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsune_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_fsne_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_mulr_q_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_maddr_q_df, void, env, i32, i32, i32, i32) +DEF_HELPER_5(msa_msubr_q_df, void, env, i32, i32, i32, i32) + +DEF_HELPER_4(msa_and_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_or_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_nor_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_xor_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bmnz_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bmz_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_bsel_v, void, env, i32, i32, i32) +DEF_HELPER_4(msa_fill_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_pcnt_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_nloc_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_nlzc_df, void, env, i32, i32, i32) + +DEF_HELPER_4(msa_fclass_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ftrunc_s_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ftrunc_u_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_fsqrt_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_frsqrt_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_frcp_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_frint_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_flog2_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_fexupl_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_fexupr_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ffql_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ffqr_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ftint_s_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ftint_u_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ffint_s_df, void, env, i32, i32, i32) +DEF_HELPER_4(msa_ffint_u_df, void, env, i32, i32, i32) + +#define MSALDST_PROTO(type)                         \ +DEF_HELPER_3(msa_ld_ ## type, void, env, i32, tl)   \ +DEF_HELPER_3(msa_st_ ## type, void, env, i32, tl) +MSALDST_PROTO(b) +MSALDST_PROTO(h) +MSALDST_PROTO(w) +MSALDST_PROTO(d) +#undef MSALDST_PROTO diff --git a/target-mips/kvm.c b/target-mips/kvm.c new file mode 100644 index 00000000..d287d428 --- /dev/null +++ b/target-mips/kvm.c @@ -0,0 +1,689 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file "COPYING" in the main directory of this archive + * for more details. + * + * KVM/MIPS: MIPS specific KVM APIs + * + * Copyright (C) 2012-2014 Imagination Technologies Ltd. + * Authors: Sanjay Lal <sanjayl@kymasys.com> +*/ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/mman.h> + +#include <linux/kvm.h> + +#include "qemu-common.h" +#include "qemu/error-report.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "cpu.h" +#include "sysemu/cpus.h" +#include "kvm_mips.h" +#include "exec/memattrs.h" + +#define DEBUG_KVM 0 + +#define DPRINTF(fmt, ...) \ +    do { if (DEBUG_KVM) { fprintf(stderr, fmt, ## __VA_ARGS__); } } while (0) + +const KVMCapabilityInfo kvm_arch_required_capabilities[] = { +    KVM_CAP_LAST_INFO +}; + +static void kvm_mips_update_state(void *opaque, int running, RunState state); + +unsigned long kvm_arch_vcpu_id(CPUState *cs) +{ +    return cs->cpu_index; +} + +int kvm_arch_init(MachineState *ms, KVMState *s) +{ +    /* MIPS has 128 signals */ +    kvm_set_sigmask_len(s, 16); + +    DPRINTF("%s\n", __func__); +    return 0; +} + +int kvm_arch_init_vcpu(CPUState *cs) +{ +    int ret = 0; + +    qemu_add_vm_change_state_handler(kvm_mips_update_state, cs); + +    DPRINTF("%s\n", __func__); +    return ret; +} + +void kvm_mips_reset_vcpu(MIPSCPU *cpu) +{ +    CPUMIPSState *env = &cpu->env; + +    if (env->CP0_Config1 & (1 << CP0C1_FP)) { +        fprintf(stderr, "Warning: FPU not supported with KVM, disabling\n"); +        env->CP0_Config1 &= ~(1 << CP0C1_FP); +    } + +    DPRINTF("%s\n", __func__); +} + +int kvm_arch_insert_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ +    DPRINTF("%s\n", __func__); +    return 0; +} + +int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) +{ +    DPRINTF("%s\n", __func__); +    return 0; +} + +static inline int cpu_mips_io_interrupts_pending(MIPSCPU *cpu) +{ +    CPUMIPSState *env = &cpu->env; + +    DPRINTF("%s: %#x\n", __func__, env->CP0_Cause & (1 << (2 + CP0Ca_IP))); +    return env->CP0_Cause & (0x1 << (2 + CP0Ca_IP)); +} + + +void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    int r; +    struct kvm_mips_interrupt intr; + +    qemu_mutex_lock_iothread(); + +    if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && +            cpu_mips_io_interrupts_pending(cpu)) { +        intr.cpu = -1; +        intr.irq = 2; +        r = kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &intr); +        if (r < 0) { +            error_report("%s: cpu %d: failed to inject IRQ %x", +                         __func__, cs->cpu_index, intr.irq); +        } +    } + +    qemu_mutex_unlock_iothread(); +} + +MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) +{ +    DPRINTF("%s\n", __func__); +    return MEMTXATTRS_UNSPECIFIED; +} + +int kvm_arch_process_async_events(CPUState *cs) +{ +    return cs->halted; +} + +int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) +{ +    int ret; + +    DPRINTF("%s\n", __func__); +    switch (run->exit_reason) { +    default: +        error_report("%s: unknown exit reason %d", +                     __func__, run->exit_reason); +        ret = -1; +        break; +    } + +    return ret; +} + +bool kvm_arch_stop_on_emulation_error(CPUState *cs) +{ +    DPRINTF("%s\n", __func__); +    return true; +} + +int kvm_arch_on_sigbus_vcpu(CPUState *cs, int code, void *addr) +{ +    DPRINTF("%s\n", __func__); +    return 1; +} + +int kvm_arch_on_sigbus(int code, void *addr) +{ +    DPRINTF("%s\n", __func__); +    return 1; +} + +void kvm_arch_init_irq_routing(KVMState *s) +{ +} + +int kvm_mips_set_interrupt(MIPSCPU *cpu, int irq, int level) +{ +    CPUState *cs = CPU(cpu); +    struct kvm_mips_interrupt intr; + +    if (!kvm_enabled()) { +        return 0; +    } + +    intr.cpu = -1; + +    if (level) { +        intr.irq = irq; +    } else { +        intr.irq = -irq; +    } + +    kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &intr); + +    return 0; +} + +int kvm_mips_set_ipi_interrupt(MIPSCPU *cpu, int irq, int level) +{ +    CPUState *cs = current_cpu; +    CPUState *dest_cs = CPU(cpu); +    struct kvm_mips_interrupt intr; + +    if (!kvm_enabled()) { +        return 0; +    } + +    intr.cpu = dest_cs->cpu_index; + +    if (level) { +        intr.irq = irq; +    } else { +        intr.irq = -irq; +    } + +    DPRINTF("%s: CPU %d, IRQ: %d\n", __func__, intr.cpu, intr.irq); + +    kvm_vcpu_ioctl(cs, KVM_INTERRUPT, &intr); + +    return 0; +} + +#define MIPS_CP0_32(_R, _S)                                     \ +    (KVM_REG_MIPS_CP0 | KVM_REG_SIZE_U32 | (8 * (_R) + (_S))) + +#define MIPS_CP0_64(_R, _S)                                     \ +    (KVM_REG_MIPS_CP0 | KVM_REG_SIZE_U64 | (8 * (_R) + (_S))) + +#define KVM_REG_MIPS_CP0_INDEX          MIPS_CP0_32(0, 0) +#define KVM_REG_MIPS_CP0_CONTEXT        MIPS_CP0_64(4, 0) +#define KVM_REG_MIPS_CP0_USERLOCAL      MIPS_CP0_64(4, 2) +#define KVM_REG_MIPS_CP0_PAGEMASK       MIPS_CP0_32(5, 0) +#define KVM_REG_MIPS_CP0_WIRED          MIPS_CP0_32(6, 0) +#define KVM_REG_MIPS_CP0_HWRENA         MIPS_CP0_32(7, 0) +#define KVM_REG_MIPS_CP0_BADVADDR       MIPS_CP0_64(8, 0) +#define KVM_REG_MIPS_CP0_COUNT          MIPS_CP0_32(9, 0) +#define KVM_REG_MIPS_CP0_ENTRYHI        MIPS_CP0_64(10, 0) +#define KVM_REG_MIPS_CP0_COMPARE        MIPS_CP0_32(11, 0) +#define KVM_REG_MIPS_CP0_STATUS         MIPS_CP0_32(12, 0) +#define KVM_REG_MIPS_CP0_CAUSE          MIPS_CP0_32(13, 0) +#define KVM_REG_MIPS_CP0_EPC            MIPS_CP0_64(14, 0) +#define KVM_REG_MIPS_CP0_ERROREPC       MIPS_CP0_64(30, 0) + +static inline int kvm_mips_put_one_reg(CPUState *cs, uint64_t reg_id, +                                       int32_t *addr) +{ +    struct kvm_one_reg cp0reg = { +        .id = reg_id, +        .addr = (uintptr_t)addr +    }; + +    return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg); +} + +static inline int kvm_mips_put_one_ulreg(CPUState *cs, uint64_t reg_id, +                                         target_ulong *addr) +{ +    uint64_t val64 = *addr; +    struct kvm_one_reg cp0reg = { +        .id = reg_id, +        .addr = (uintptr_t)&val64 +    }; + +    return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg); +} + +static inline int kvm_mips_put_one_reg64(CPUState *cs, uint64_t reg_id, +                                         uint64_t *addr) +{ +    struct kvm_one_reg cp0reg = { +        .id = reg_id, +        .addr = (uintptr_t)addr +    }; + +    return kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, &cp0reg); +} + +static inline int kvm_mips_get_one_reg(CPUState *cs, uint64_t reg_id, +                                       int32_t *addr) +{ +    struct kvm_one_reg cp0reg = { +        .id = reg_id, +        .addr = (uintptr_t)addr +    }; + +    return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg); +} + +static inline int kvm_mips_get_one_ulreg(CPUState *cs, uint64 reg_id, +                                         target_ulong *addr) +{ +    int ret; +    uint64_t val64 = 0; +    struct kvm_one_reg cp0reg = { +        .id = reg_id, +        .addr = (uintptr_t)&val64 +    }; + +    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg); +    if (ret >= 0) { +        *addr = val64; +    } +    return ret; +} + +static inline int kvm_mips_get_one_reg64(CPUState *cs, uint64 reg_id, +                                         uint64_t *addr) +{ +    struct kvm_one_reg cp0reg = { +        .id = reg_id, +        .addr = (uintptr_t)addr +    }; + +    return kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, &cp0reg); +} + +/* + * We freeze the KVM timer when either the VM clock is stopped or the state is + * saved (the state is dirty). + */ + +/* + * Save the state of the KVM timer when VM clock is stopped or state is synced + * to QEMU. + */ +static int kvm_mips_save_count(CPUState *cs) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    uint64_t count_ctl; +    int err, ret = 0; + +    /* freeze KVM timer */ +    err = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); +    if (err < 0) { +        DPRINTF("%s: Failed to get COUNT_CTL (%d)\n", __func__, err); +        ret = err; +    } else if (!(count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) { +        count_ctl |= KVM_REG_MIPS_COUNT_CTL_DC; +        err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); +        if (err < 0) { +            DPRINTF("%s: Failed to set COUNT_CTL.DC=1 (%d)\n", __func__, err); +            ret = err; +        } +    } + +    /* read CP0_Cause */ +    err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_CAUSE, &env->CP0_Cause); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_CAUSE (%d)\n", __func__, err); +        ret = err; +    } + +    /* read CP0_Count */ +    err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_COUNT, &env->CP0_Count); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_COUNT (%d)\n", __func__, err); +        ret = err; +    } + +    return ret; +} + +/* + * Restore the state of the KVM timer when VM clock is restarted or state is + * synced to KVM. + */ +static int kvm_mips_restore_count(CPUState *cs) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    uint64_t count_ctl; +    int err_dc, err, ret = 0; + +    /* check the timer is frozen */ +    err_dc = kvm_mips_get_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); +    if (err_dc < 0) { +        DPRINTF("%s: Failed to get COUNT_CTL (%d)\n", __func__, err_dc); +        ret = err_dc; +    } else if (!(count_ctl & KVM_REG_MIPS_COUNT_CTL_DC)) { +        /* freeze timer (sets COUNT_RESUME for us) */ +        count_ctl |= KVM_REG_MIPS_COUNT_CTL_DC; +        err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); +        if (err < 0) { +            DPRINTF("%s: Failed to set COUNT_CTL.DC=1 (%d)\n", __func__, err); +            ret = err; +        } +    } + +    /* load CP0_Cause */ +    err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_CAUSE, &env->CP0_Cause); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_CAUSE (%d)\n", __func__, err); +        ret = err; +    } + +    /* load CP0_Count */ +    err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_COUNT, &env->CP0_Count); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_COUNT (%d)\n", __func__, err); +        ret = err; +    } + +    /* resume KVM timer */ +    if (err_dc >= 0) { +        count_ctl &= ~KVM_REG_MIPS_COUNT_CTL_DC; +        err = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_CTL, &count_ctl); +        if (err < 0) { +            DPRINTF("%s: Failed to set COUNT_CTL.DC=0 (%d)\n", __func__, err); +            ret = err; +        } +    } + +    return ret; +} + +/* + * Handle the VM clock being started or stopped + */ +static void kvm_mips_update_state(void *opaque, int running, RunState state) +{ +    CPUState *cs = opaque; +    int ret; +    uint64_t count_resume; + +    /* +     * If state is already dirty (synced to QEMU) then the KVM timer state is +     * already saved and can be restored when it is synced back to KVM. +     */ +    if (!running) { +        if (!cs->kvm_vcpu_dirty) { +            ret = kvm_mips_save_count(cs); +            if (ret < 0) { +                fprintf(stderr, "Failed saving count\n"); +            } +        } +    } else { +        /* Set clock restore time to now */ +        count_resume = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); +        ret = kvm_mips_put_one_reg64(cs, KVM_REG_MIPS_COUNT_RESUME, +                                     &count_resume); +        if (ret < 0) { +            fprintf(stderr, "Failed setting COUNT_RESUME\n"); +            return; +        } + +        if (!cs->kvm_vcpu_dirty) { +            ret = kvm_mips_restore_count(cs); +            if (ret < 0) { +                fprintf(stderr, "Failed restoring count\n"); +            } +        } +    } +} + +static int kvm_mips_put_cp0_registers(CPUState *cs, int level) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    int err, ret = 0; + +    (void)level; + +    err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_INDEX, &env->CP0_Index); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_INDEX (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_CONTEXT, +                                 &env->CP0_Context); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_CONTEXT (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_USERLOCAL, +                                 &env->active_tc.CP0_UserLocal); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_USERLOCAL (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_PAGEMASK, +                               &env->CP0_PageMask); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_PAGEMASK (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_WIRED, &env->CP0_Wired); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_WIRED (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_HWRENA, &env->CP0_HWREna); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_HWRENA (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_BADVADDR, +                                 &env->CP0_BadVAddr); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_BADVADDR (%d)\n", __func__, err); +        ret = err; +    } + +    /* If VM clock stopped then state will be restored when it is restarted */ +    if (runstate_is_running()) { +        err = kvm_mips_restore_count(cs); +        if (err < 0) { +            ret = err; +        } +    } + +    err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_ENTRYHI, +                                 &env->CP0_EntryHi); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_ENTRYHI (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_COMPARE, +                               &env->CP0_Compare); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_COMPARE (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_put_one_reg(cs, KVM_REG_MIPS_CP0_STATUS, &env->CP0_Status); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_STATUS (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_EPC, &env->CP0_EPC); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_EPC (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_put_one_ulreg(cs, KVM_REG_MIPS_CP0_ERROREPC, +                                 &env->CP0_ErrorEPC); +    if (err < 0) { +        DPRINTF("%s: Failed to put CP0_ERROREPC (%d)\n", __func__, err); +        ret = err; +    } + +    return ret; +} + +static int kvm_mips_get_cp0_registers(CPUState *cs) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    int err, ret = 0; + +    err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_INDEX, &env->CP0_Index); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_INDEX (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_CONTEXT, +                                 &env->CP0_Context); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_CONTEXT (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_USERLOCAL, +                                 &env->active_tc.CP0_UserLocal); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_USERLOCAL (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_PAGEMASK, +                               &env->CP0_PageMask); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_PAGEMASK (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_WIRED, &env->CP0_Wired); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_WIRED (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_HWRENA, &env->CP0_HWREna); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_HWRENA (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_BADVADDR, +                                 &env->CP0_BadVAddr); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_BADVADDR (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_ENTRYHI, +                                 &env->CP0_EntryHi); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_ENTRYHI (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_COMPARE, +                               &env->CP0_Compare); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_COMPARE (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_get_one_reg(cs, KVM_REG_MIPS_CP0_STATUS, &env->CP0_Status); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_STATUS (%d)\n", __func__, err); +        ret = err; +    } + +    /* If VM clock stopped then state was already saved when it was stopped */ +    if (runstate_is_running()) { +        err = kvm_mips_save_count(cs); +        if (err < 0) { +            ret = err; +        } +    } + +    err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_EPC, &env->CP0_EPC); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_EPC (%d)\n", __func__, err); +        ret = err; +    } +    err = kvm_mips_get_one_ulreg(cs, KVM_REG_MIPS_CP0_ERROREPC, +                                 &env->CP0_ErrorEPC); +    if (err < 0) { +        DPRINTF("%s: Failed to get CP0_ERROREPC (%d)\n", __func__, err); +        ret = err; +    } + +    return ret; +} + +int kvm_arch_put_registers(CPUState *cs, int level) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    struct kvm_regs regs; +    int ret; +    int i; + +    /* Set the registers based on QEMU's view of things */ +    for (i = 0; i < 32; i++) { +        regs.gpr[i] = (int64_t)(target_long)env->active_tc.gpr[i]; +    } + +    regs.hi = (int64_t)(target_long)env->active_tc.HI[0]; +    regs.lo = (int64_t)(target_long)env->active_tc.LO[0]; +    regs.pc = (int64_t)(target_long)env->active_tc.PC; + +    ret = kvm_vcpu_ioctl(cs, KVM_SET_REGS, ®s); + +    if (ret < 0) { +        return ret; +    } + +    ret = kvm_mips_put_cp0_registers(cs, level); +    if (ret < 0) { +        return ret; +    } + +    return ret; +} + +int kvm_arch_get_registers(CPUState *cs) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    int ret = 0; +    struct kvm_regs regs; +    int i; + +    /* Get the current register set as KVM seems it */ +    ret = kvm_vcpu_ioctl(cs, KVM_GET_REGS, ®s); + +    if (ret < 0) { +        return ret; +    } + +    for (i = 0; i < 32; i++) { +        env->active_tc.gpr[i] = regs.gpr[i]; +    } + +    env->active_tc.HI[0] = regs.hi; +    env->active_tc.LO[0] = regs.lo; +    env->active_tc.PC = regs.pc; + +    kvm_mips_get_cp0_registers(cs); + +    return ret; +} + +int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, +                             uint64_t address, uint32_t data) +{ +    return 0; +} + +int kvm_arch_msi_data_to_gsi(uint32_t data) +{ +    abort(); +} diff --git a/target-mips/kvm_mips.h b/target-mips/kvm_mips.h new file mode 100644 index 00000000..54f59656 --- /dev/null +++ b/target-mips/kvm_mips.h @@ -0,0 +1,26 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License.  See the file "COPYING" in the main directory of this archive + * for more details. + * + * KVM/MIPS: MIPS specific KVM APIs + * + * Copyright (C) 2012-2014 Imagination Technologies Ltd. + * Authors: Sanjay Lal <sanjayl@kymasys.com> +*/ + +#ifndef __KVM_MIPS_H__ +#define __KVM_MIPS_H__ + +/** + * kvm_mips_reset_vcpu: + * @cpu: MIPSCPU + * + * Called at reset time to set kernel registers to their initial values. + */ +void kvm_mips_reset_vcpu(MIPSCPU *cpu); + +int kvm_mips_set_interrupt(MIPSCPU *cpu, int irq, int level); +int kvm_mips_set_ipi_interrupt(MIPSCPU *cpu, int irq, int level); + +#endif /* __KVM_MIPS_H__ */ diff --git a/target-mips/lmi_helper.c b/target-mips/lmi_helper.c new file mode 100644 index 00000000..bbfcd59c --- /dev/null +++ b/target-mips/lmi_helper.c @@ -0,0 +1,744 @@ +/* + *  Loongson Multimedia Instruction emulation helpers for QEMU. + * + *  Copyright (c) 2011  Richard Henderson <rth@twiddle.net> + * + * 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 "cpu.h" +#include "exec/helper-proto.h" + +/* If the byte ordering doesn't matter, i.e. all columns are treated +   identically, then this union can be used directly.  If byte ordering +   does matter, we generally ignore dumping to memory.  */ +typedef union { +    uint8_t  ub[8]; +    int8_t   sb[8]; +    uint16_t uh[4]; +    int16_t  sh[4]; +    uint32_t uw[2]; +    int32_t  sw[2]; +    uint64_t d; +} LMIValue; + +/* Some byte ordering issues can be mitigated by XORing in the following.  */ +#ifdef HOST_WORDS_BIGENDIAN +# define BYTE_ORDER_XOR(N) N +#else +# define BYTE_ORDER_XOR(N) 0 +#endif + +#define SATSB(x)  (x < -0x80 ? -0x80 : x > 0x7f ? 0x7f : x) +#define SATUB(x)  (x > 0xff ? 0xff : x) + +#define SATSH(x)  (x < -0x8000 ? -0x8000 : x > 0x7fff ? 0x7fff : x) +#define SATUH(x)  (x > 0xffff ? 0xffff : x) + +#define SATSW(x) \ +    (x < -0x80000000ll ? -0x80000000ll : x > 0x7fffffff ? 0x7fffffff : x) +#define SATUW(x)  (x > 0xffffffffull ? 0xffffffffull : x) + +uint64_t helper_paddsb(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 8; ++i) { +        int r = vs.sb[i] + vt.sb[i]; +        vs.sb[i] = SATSB(r); +    } +    return vs.d; +} + +uint64_t helper_paddusb(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 8; ++i) { +        int r = vs.ub[i] + vt.ub[i]; +        vs.ub[i] = SATUB(r); +    } +    return vs.d; +} + +uint64_t helper_paddsh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; ++i) { +        int r = vs.sh[i] + vt.sh[i]; +        vs.sh[i] = SATSH(r); +    } +    return vs.d; +} + +uint64_t helper_paddush(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; ++i) { +        int r = vs.uh[i] + vt.uh[i]; +        vs.uh[i] = SATUH(r); +    } +    return vs.d; +} + +uint64_t helper_paddb(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 8; ++i) { +        vs.ub[i] += vt.ub[i]; +    } +    return vs.d; +} + +uint64_t helper_paddh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; ++i) { +        vs.uh[i] += vt.uh[i]; +    } +    return vs.d; +} + +uint64_t helper_paddw(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 2; ++i) { +        vs.uw[i] += vt.uw[i]; +    } +    return vs.d; +} + +uint64_t helper_psubsb(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 8; ++i) { +        int r = vs.sb[i] - vt.sb[i]; +        vs.sb[i] = SATSB(r); +    } +    return vs.d; +} + +uint64_t helper_psubusb(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 8; ++i) { +        int r = vs.ub[i] - vt.ub[i]; +        vs.ub[i] = SATUB(r); +    } +    return vs.d; +} + +uint64_t helper_psubsh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; ++i) { +        int r = vs.sh[i] - vt.sh[i]; +        vs.sh[i] = SATSH(r); +    } +    return vs.d; +} + +uint64_t helper_psubush(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; ++i) { +        int r = vs.uh[i] - vt.uh[i]; +        vs.uh[i] = SATUH(r); +    } +    return vs.d; +} + +uint64_t helper_psubb(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 8; ++i) { +        vs.ub[i] -= vt.ub[i]; +    } +    return vs.d; +} + +uint64_t helper_psubh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; ++i) { +        vs.uh[i] -= vt.uh[i]; +    } +    return vs.d; +} + +uint64_t helper_psubw(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned int i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 2; ++i) { +        vs.uw[i] -= vt.uw[i]; +    } +    return vs.d; +} + +uint64_t helper_pshufh(uint64_t fs, uint64_t ft) +{ +    unsigned host = BYTE_ORDER_XOR(3); +    LMIValue vd, vs; +    unsigned i; + +    vs.d = fs; +    vd.d = 0; +    for (i = 0; i < 4; i++, ft >>= 2) { +        vd.uh[i ^ host] = vs.uh[(ft & 3) ^ host]; +    } +    return vd.d; +} + +uint64_t helper_packsswh(uint64_t fs, uint64_t ft) +{ +    uint64_t fd = 0; +    int64_t tmp; + +    tmp = (int32_t)(fs >> 0); +    tmp = SATSH(tmp); +    fd |= (tmp & 0xffff) << 0; + +    tmp = (int32_t)(fs >> 32); +    tmp = SATSH(tmp); +    fd |= (tmp & 0xffff) << 16; + +    tmp = (int32_t)(ft >> 0); +    tmp = SATSH(tmp); +    fd |= (tmp & 0xffff) << 32; + +    tmp = (int32_t)(ft >> 32); +    tmp = SATSH(tmp); +    fd |= (tmp & 0xffff) << 48; + +    return fd; +} + +uint64_t helper_packsshb(uint64_t fs, uint64_t ft) +{ +    uint64_t fd = 0; +    unsigned int i; + +    for (i = 0; i < 4; ++i) { +        int16_t tmp = fs >> (i * 16); +        tmp = SATSB(tmp); +        fd |= (uint64_t)(tmp & 0xff) << (i * 8); +    } +    for (i = 0; i < 4; ++i) { +        int16_t tmp = ft >> (i * 16); +        tmp = SATSB(tmp); +        fd |= (uint64_t)(tmp & 0xff) << (i * 8 + 32); +    } + +    return fd; +} + +uint64_t helper_packushb(uint64_t fs, uint64_t ft) +{ +    uint64_t fd = 0; +    unsigned int i; + +    for (i = 0; i < 4; ++i) { +        int16_t tmp = fs >> (i * 16); +        tmp = SATUB(tmp); +        fd |= (uint64_t)(tmp & 0xff) << (i * 8); +    } +    for (i = 0; i < 4; ++i) { +        int16_t tmp = ft >> (i * 16); +        tmp = SATUB(tmp); +        fd |= (uint64_t)(tmp & 0xff) << (i * 8 + 32); +    } + +    return fd; +} + +uint64_t helper_punpcklwd(uint64_t fs, uint64_t ft) +{ +    return (fs & 0xffffffff) | (ft << 32); +} + +uint64_t helper_punpckhwd(uint64_t fs, uint64_t ft) +{ +    return (fs >> 32) | (ft & ~0xffffffffull); +} + +uint64_t helper_punpcklhw(uint64_t fs, uint64_t ft) +{ +    unsigned host = BYTE_ORDER_XOR(3); +    LMIValue vd, vs, vt; + +    vs.d = fs; +    vt.d = ft; +    vd.uh[0 ^ host] = vs.uh[0 ^ host]; +    vd.uh[1 ^ host] = vt.uh[0 ^ host]; +    vd.uh[2 ^ host] = vs.uh[1 ^ host]; +    vd.uh[3 ^ host] = vt.uh[1 ^ host]; + +    return vd.d; +} + +uint64_t helper_punpckhhw(uint64_t fs, uint64_t ft) +{ +    unsigned host = BYTE_ORDER_XOR(3); +    LMIValue vd, vs, vt; + +    vs.d = fs; +    vt.d = ft; +    vd.uh[0 ^ host] = vs.uh[2 ^ host]; +    vd.uh[1 ^ host] = vt.uh[2 ^ host]; +    vd.uh[2 ^ host] = vs.uh[3 ^ host]; +    vd.uh[3 ^ host] = vt.uh[3 ^ host]; + +    return vd.d; +} + +uint64_t helper_punpcklbh(uint64_t fs, uint64_t ft) +{ +    unsigned host = BYTE_ORDER_XOR(7); +    LMIValue vd, vs, vt; + +    vs.d = fs; +    vt.d = ft; +    vd.ub[0 ^ host] = vs.ub[0 ^ host]; +    vd.ub[1 ^ host] = vt.ub[0 ^ host]; +    vd.ub[2 ^ host] = vs.ub[1 ^ host]; +    vd.ub[3 ^ host] = vt.ub[1 ^ host]; +    vd.ub[4 ^ host] = vs.ub[2 ^ host]; +    vd.ub[5 ^ host] = vt.ub[2 ^ host]; +    vd.ub[6 ^ host] = vs.ub[3 ^ host]; +    vd.ub[7 ^ host] = vt.ub[3 ^ host]; + +    return vd.d; +} + +uint64_t helper_punpckhbh(uint64_t fs, uint64_t ft) +{ +    unsigned host = BYTE_ORDER_XOR(7); +    LMIValue vd, vs, vt; + +    vs.d = fs; +    vt.d = ft; +    vd.ub[0 ^ host] = vs.ub[4 ^ host]; +    vd.ub[1 ^ host] = vt.ub[4 ^ host]; +    vd.ub[2 ^ host] = vs.ub[5 ^ host]; +    vd.ub[3 ^ host] = vt.ub[5 ^ host]; +    vd.ub[4 ^ host] = vs.ub[6 ^ host]; +    vd.ub[5 ^ host] = vt.ub[6 ^ host]; +    vd.ub[6 ^ host] = vs.ub[7 ^ host]; +    vd.ub[7 ^ host] = vt.ub[7 ^ host]; + +    return vd.d; +} + +uint64_t helper_pavgh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; i++) { +        vs.uh[i] = (vs.uh[i] + vt.uh[i] + 1) >> 1; +    } +    return vs.d; +} + +uint64_t helper_pavgb(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 8; i++) { +        vs.ub[i] = (vs.ub[i] + vt.ub[i] + 1) >> 1; +    } +    return vs.d; +} + +uint64_t helper_pmaxsh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; i++) { +        vs.sh[i] = (vs.sh[i] >= vt.sh[i] ? vs.sh[i] : vt.sh[i]); +    } +    return vs.d; +} + +uint64_t helper_pminsh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; i++) { +        vs.sh[i] = (vs.sh[i] <= vt.sh[i] ? vs.sh[i] : vt.sh[i]); +    } +    return vs.d; +} + +uint64_t helper_pmaxub(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; i++) { +        vs.ub[i] = (vs.ub[i] >= vt.ub[i] ? vs.ub[i] : vt.ub[i]); +    } +    return vs.d; +} + +uint64_t helper_pminub(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; i++) { +        vs.ub[i] = (vs.ub[i] <= vt.ub[i] ? vs.ub[i] : vt.ub[i]); +    } +    return vs.d; +} + +uint64_t helper_pcmpeqw(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 2; i++) { +        vs.uw[i] = -(vs.uw[i] == vt.uw[i]); +    } +    return vs.d; +} + +uint64_t helper_pcmpgtw(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 2; i++) { +        vs.uw[i] = -(vs.uw[i] > vt.uw[i]); +    } +    return vs.d; +} + +uint64_t helper_pcmpeqh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; i++) { +        vs.uh[i] = -(vs.uh[i] == vt.uh[i]); +    } +    return vs.d; +} + +uint64_t helper_pcmpgth(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; i++) { +        vs.uh[i] = -(vs.uh[i] > vt.uh[i]); +    } +    return vs.d; +} + +uint64_t helper_pcmpeqb(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 8; i++) { +        vs.ub[i] = -(vs.ub[i] == vt.ub[i]); +    } +    return vs.d; +} + +uint64_t helper_pcmpgtb(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 8; i++) { +        vs.ub[i] = -(vs.ub[i] > vt.ub[i]); +    } +    return vs.d; +} + +uint64_t helper_psllw(uint64_t fs, uint64_t ft) +{ +    LMIValue vs; +    unsigned i; + +    ft &= 0x7f; +    if (ft > 31) { +        return 0; +    } +    vs.d = fs; +    for (i = 0; i < 2; ++i) { +        vs.uw[i] <<= ft; +    } +    return vs.d; +} + +uint64_t helper_psrlw(uint64_t fs, uint64_t ft) +{ +    LMIValue vs; +    unsigned i; + +    ft &= 0x7f; +    if (ft > 31) { +        return 0; +    } +    vs.d = fs; +    for (i = 0; i < 2; ++i) { +        vs.uw[i] >>= ft; +    } +    return vs.d; +} + +uint64_t helper_psraw(uint64_t fs, uint64_t ft) +{ +    LMIValue vs; +    unsigned i; + +    ft &= 0x7f; +    if (ft > 31) { +        ft = 31; +    } +    vs.d = fs; +    for (i = 0; i < 2; ++i) { +        vs.sw[i] >>= ft; +    } +    return vs.d; +} + +uint64_t helper_psllh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs; +    unsigned i; + +    ft &= 0x7f; +    if (ft > 15) { +        return 0; +    } +    vs.d = fs; +    for (i = 0; i < 4; ++i) { +        vs.uh[i] <<= ft; +    } +    return vs.d; +} + +uint64_t helper_psrlh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs; +    unsigned i; + +    ft &= 0x7f; +    if (ft > 15) { +        return 0; +    } +    vs.d = fs; +    for (i = 0; i < 4; ++i) { +        vs.uh[i] >>= ft; +    } +    return vs.d; +} + +uint64_t helper_psrah(uint64_t fs, uint64_t ft) +{ +    LMIValue vs; +    unsigned i; + +    ft &= 0x7f; +    if (ft > 15) { +        ft = 15; +    } +    vs.d = fs; +    for (i = 0; i < 4; ++i) { +        vs.sh[i] >>= ft; +    } +    return vs.d; +} + +uint64_t helper_pmullh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; ++i) { +        vs.sh[i] *= vt.sh[i]; +    } +    return vs.d; +} + +uint64_t helper_pmulhh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; ++i) { +        int32_t r = vs.sh[i] * vt.sh[i]; +        vs.sh[i] = r >> 16; +    } +    return vs.d; +} + +uint64_t helper_pmulhuh(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 4; ++i) { +        uint32_t r = vs.uh[i] * vt.uh[i]; +        vs.uh[i] = r >> 16; +    } +    return vs.d; +} + +uint64_t helper_pmaddhw(uint64_t fs, uint64_t ft) +{ +    unsigned host = BYTE_ORDER_XOR(3); +    LMIValue vs, vt; +    uint32_t p0, p1; + +    vs.d = fs; +    vt.d = ft; +    p0  = vs.sh[0 ^ host] * vt.sh[0 ^ host]; +    p0 += vs.sh[1 ^ host] * vt.sh[1 ^ host]; +    p1  = vs.sh[2 ^ host] * vt.sh[2 ^ host]; +    p1 += vs.sh[3 ^ host] * vt.sh[3 ^ host]; + +    return ((uint64_t)p1 << 32) | p0; +} + +uint64_t helper_pasubub(uint64_t fs, uint64_t ft) +{ +    LMIValue vs, vt; +    unsigned i; + +    vs.d = fs; +    vt.d = ft; +    for (i = 0; i < 8; ++i) { +        int r = vs.ub[i] - vt.ub[i]; +        vs.ub[i] = (r < 0 ? -r : r); +    } +    return vs.d; +} + +uint64_t helper_biadd(uint64_t fs) +{ +    unsigned i, fd; + +    for (i = fd = 0; i < 8; ++i) { +        fd += (fs >> (i * 8)) & 0xff; +    } +    return fd & 0xffff; +} + +uint64_t helper_pmovmskb(uint64_t fs) +{ +    unsigned fd = 0; + +    fd |= ((fs >>  7) & 1) << 0; +    fd |= ((fs >> 15) & 1) << 1; +    fd |= ((fs >> 23) & 1) << 2; +    fd |= ((fs >> 31) & 1) << 3; +    fd |= ((fs >> 39) & 1) << 4; +    fd |= ((fs >> 47) & 1) << 5; +    fd |= ((fs >> 55) & 1) << 6; +    fd |= ((fs >> 63) & 1) << 7; + +    return fd & 0xff; +} diff --git a/target-mips/machine.c b/target-mips/machine.c new file mode 100644 index 00000000..b15c43a1 --- /dev/null +++ b/target-mips/machine.c @@ -0,0 +1,298 @@ +#include "hw/hw.h" + +#include "cpu.h" + +static int cpu_post_load(void *opaque, int version_id) +{ +    MIPSCPU *cpu = opaque; +    CPUMIPSState *env = &cpu->env; + +    restore_fp_status(env); +    restore_msa_fp_status(env); +    compute_hflags(env); +    restore_pamask(env); + +    return 0; +} + +/* FPU state */ + +static int get_fpr(QEMUFile *f, void *pv, size_t size) +{ +    int i; +    fpr_t *v = pv; +    /* Restore entire MSA vector register */ +    for (i = 0; i < MSA_WRLEN/64; i++) { +        qemu_get_sbe64s(f, &v->wr.d[i]); +    } +    return 0; +} + +static void put_fpr(QEMUFile *f, void *pv, size_t size) +{ +    int i; +    fpr_t *v = pv; +    /* Save entire MSA vector register */ +    for (i = 0; i < MSA_WRLEN/64; i++) { +        qemu_put_sbe64s(f, &v->wr.d[i]); +    } +} + +const VMStateInfo vmstate_info_fpr = { +    .name = "fpr", +    .get  = get_fpr, +    .put  = put_fpr, +}; + +#define VMSTATE_FPR_ARRAY_V(_f, _s, _n, _v)                     \ +    VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_fpr, fpr_t) + +#define VMSTATE_FPR_ARRAY(_f, _s, _n)                           \ +    VMSTATE_FPR_ARRAY_V(_f, _s, _n, 0) + +static VMStateField vmstate_fpu_fields[] = { +    VMSTATE_FPR_ARRAY(fpr, CPUMIPSFPUContext, 32), +    VMSTATE_UINT32(fcr0, CPUMIPSFPUContext), +    VMSTATE_UINT32(fcr31, CPUMIPSFPUContext), +    VMSTATE_END_OF_LIST() +}; + +const VMStateDescription vmstate_fpu = { +    .name = "cpu/fpu", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = vmstate_fpu_fields +}; + +const VMStateDescription vmstate_inactive_fpu = { +    .name = "cpu/inactive_fpu", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = vmstate_fpu_fields +}; + +/* TC state */ + +static VMStateField vmstate_tc_fields[] = { +    VMSTATE_UINTTL_ARRAY(gpr, TCState, 32), +    VMSTATE_UINTTL(PC, TCState), +    VMSTATE_UINTTL_ARRAY(HI, TCState, MIPS_DSP_ACC), +    VMSTATE_UINTTL_ARRAY(LO, TCState, MIPS_DSP_ACC), +    VMSTATE_UINTTL_ARRAY(ACX, TCState, MIPS_DSP_ACC), +    VMSTATE_UINTTL(DSPControl, TCState), +    VMSTATE_INT32(CP0_TCStatus, TCState), +    VMSTATE_INT32(CP0_TCBind, TCState), +    VMSTATE_UINTTL(CP0_TCHalt, TCState), +    VMSTATE_UINTTL(CP0_TCContext, TCState), +    VMSTATE_UINTTL(CP0_TCSchedule, TCState), +    VMSTATE_UINTTL(CP0_TCScheFBack, TCState), +    VMSTATE_INT32(CP0_Debug_tcstatus, TCState), +    VMSTATE_UINTTL(CP0_UserLocal, TCState), +    VMSTATE_INT32(msacsr, TCState), +    VMSTATE_END_OF_LIST() +}; + +const VMStateDescription vmstate_tc = { +    .name = "cpu/tc", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = vmstate_tc_fields +}; + +const VMStateDescription vmstate_inactive_tc = { +    .name = "cpu/inactive_tc", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = vmstate_tc_fields +}; + +/* MVP state */ + +const VMStateDescription vmstate_mvp = { +    .name = "cpu/mvp", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = (VMStateField[]) { +        VMSTATE_INT32(CP0_MVPControl, CPUMIPSMVPContext), +        VMSTATE_INT32(CP0_MVPConf0, CPUMIPSMVPContext), +        VMSTATE_INT32(CP0_MVPConf1, CPUMIPSMVPContext), +        VMSTATE_END_OF_LIST() +    } +}; + +/* TLB state */ + +static int get_tlb(QEMUFile *f, void *pv, size_t size) +{ +    r4k_tlb_t *v = pv; +    uint16_t flags; + +    qemu_get_betls(f, &v->VPN); +    qemu_get_be32s(f, &v->PageMask); +    qemu_get_8s(f, &v->ASID); +    qemu_get_be16s(f, &flags); +    v->G = (flags >> 10) & 1; +    v->C0 = (flags >> 7) & 3; +    v->C1 = (flags >> 4) & 3; +    v->V0 = (flags >> 3) & 1; +    v->V1 = (flags >> 2) & 1; +    v->D0 = (flags >> 1) & 1; +    v->D1 = (flags >> 0) & 1; +    v->EHINV = (flags >> 15) & 1; +    v->RI1 = (flags >> 14) & 1; +    v->RI0 = (flags >> 13) & 1; +    v->XI1 = (flags >> 12) & 1; +    v->XI0 = (flags >> 11) & 1; +    qemu_get_be64s(f, &v->PFN[0]); +    qemu_get_be64s(f, &v->PFN[1]); + +    return 0; +} + +static void put_tlb(QEMUFile *f, void *pv, size_t size) +{ +    r4k_tlb_t *v = pv; + +    uint8_t asid = v->ASID; +    uint16_t flags = ((v->EHINV << 15) | +                      (v->RI1 << 14) | +                      (v->RI0 << 13) | +                      (v->XI1 << 12) | +                      (v->XI0 << 11) | +                      (v->G << 10) | +                      (v->C0 << 7) | +                      (v->C1 << 4) | +                      (v->V0 << 3) | +                      (v->V1 << 2) | +                      (v->D0 << 1) | +                      (v->D1 << 0)); + +    qemu_put_betls(f, &v->VPN); +    qemu_put_be32s(f, &v->PageMask); +    qemu_put_8s(f, &asid); +    qemu_put_be16s(f, &flags); +    qemu_put_be64s(f, &v->PFN[0]); +    qemu_put_be64s(f, &v->PFN[1]); +} + +const VMStateInfo vmstate_info_tlb = { +    .name = "tlb_entry", +    .get  = get_tlb, +    .put  = put_tlb, +}; + +#define VMSTATE_TLB_ARRAY_V(_f, _s, _n, _v)                     \ +    VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_tlb, r4k_tlb_t) + +#define VMSTATE_TLB_ARRAY(_f, _s, _n)                           \ +    VMSTATE_TLB_ARRAY_V(_f, _s, _n, 0) + +const VMStateDescription vmstate_tlb = { +    .name = "cpu/tlb", +    .version_id = 1, +    .minimum_version_id = 1, +    .fields = (VMStateField[]) { +        VMSTATE_UINT32(nb_tlb, CPUMIPSTLBContext), +        VMSTATE_UINT32(tlb_in_use, CPUMIPSTLBContext), +        VMSTATE_TLB_ARRAY(mmu.r4k.tlb, CPUMIPSTLBContext, MIPS_TLB_MAX), +        VMSTATE_END_OF_LIST() +    } +}; + +/* MIPS CPU state */ + +const VMStateDescription vmstate_mips_cpu = { +    .name = "cpu", +    .version_id = 7, +    .minimum_version_id = 7, +    .post_load = cpu_post_load, +    .fields = (VMStateField[]) { +        /* Active TC */ +        VMSTATE_STRUCT(env.active_tc, MIPSCPU, 1, vmstate_tc, TCState), + +        /* Active FPU */ +        VMSTATE_STRUCT(env.active_fpu, MIPSCPU, 1, vmstate_fpu, +                       CPUMIPSFPUContext), + +        /* MVP */ +        VMSTATE_STRUCT_POINTER(env.mvp, MIPSCPU, vmstate_mvp, +                               CPUMIPSMVPContext), + +        /* TLB */ +        VMSTATE_STRUCT_POINTER(env.tlb, MIPSCPU, vmstate_tlb, +                               CPUMIPSTLBContext), + +        /* CPU metastate */ +        VMSTATE_UINT32(env.current_tc, MIPSCPU), +        VMSTATE_UINT32(env.current_fpu, MIPSCPU), +        VMSTATE_INT32(env.error_code, MIPSCPU), +        VMSTATE_UINTTL(env.btarget, MIPSCPU), +        VMSTATE_UINTTL(env.bcond, MIPSCPU), + +        /* Remaining CP0 registers */ +        VMSTATE_INT32(env.CP0_Index, MIPSCPU), +        VMSTATE_INT32(env.CP0_Random, MIPSCPU), +        VMSTATE_INT32(env.CP0_VPEControl, MIPSCPU), +        VMSTATE_INT32(env.CP0_VPEConf0, MIPSCPU), +        VMSTATE_INT32(env.CP0_VPEConf1, MIPSCPU), +        VMSTATE_UINTTL(env.CP0_YQMask, MIPSCPU), +        VMSTATE_UINTTL(env.CP0_VPESchedule, MIPSCPU), +        VMSTATE_UINTTL(env.CP0_VPEScheFBack, MIPSCPU), +        VMSTATE_INT32(env.CP0_VPEOpt, MIPSCPU), +        VMSTATE_UINT64(env.CP0_EntryLo0, MIPSCPU), +        VMSTATE_UINT64(env.CP0_EntryLo1, MIPSCPU), +        VMSTATE_UINTTL(env.CP0_Context, MIPSCPU), +        VMSTATE_INT32(env.CP0_PageMask, MIPSCPU), +        VMSTATE_INT32(env.CP0_PageGrain, MIPSCPU), +        VMSTATE_INT32(env.CP0_Wired, MIPSCPU), +        VMSTATE_INT32(env.CP0_SRSConf0, MIPSCPU), +        VMSTATE_INT32(env.CP0_SRSConf1, MIPSCPU), +        VMSTATE_INT32(env.CP0_SRSConf2, MIPSCPU), +        VMSTATE_INT32(env.CP0_SRSConf3, MIPSCPU), +        VMSTATE_INT32(env.CP0_SRSConf4, MIPSCPU), +        VMSTATE_INT32(env.CP0_HWREna, MIPSCPU), +        VMSTATE_UINTTL(env.CP0_BadVAddr, MIPSCPU), +        VMSTATE_UINT32(env.CP0_BadInstr, MIPSCPU), +        VMSTATE_UINT32(env.CP0_BadInstrP, MIPSCPU), +        VMSTATE_INT32(env.CP0_Count, MIPSCPU), +        VMSTATE_UINTTL(env.CP0_EntryHi, MIPSCPU), +        VMSTATE_INT32(env.CP0_Compare, MIPSCPU), +        VMSTATE_INT32(env.CP0_Status, MIPSCPU), +        VMSTATE_INT32(env.CP0_IntCtl, MIPSCPU), +        VMSTATE_INT32(env.CP0_SRSCtl, MIPSCPU), +        VMSTATE_INT32(env.CP0_SRSMap, MIPSCPU), +        VMSTATE_INT32(env.CP0_Cause, MIPSCPU), +        VMSTATE_UINTTL(env.CP0_EPC, MIPSCPU), +        VMSTATE_INT32(env.CP0_PRid, MIPSCPU), +        VMSTATE_INT32(env.CP0_EBase, MIPSCPU), +        VMSTATE_INT32(env.CP0_Config0, MIPSCPU), +        VMSTATE_INT32(env.CP0_Config1, MIPSCPU), +        VMSTATE_INT32(env.CP0_Config2, MIPSCPU), +        VMSTATE_INT32(env.CP0_Config3, MIPSCPU), +        VMSTATE_INT32(env.CP0_Config6, MIPSCPU), +        VMSTATE_INT32(env.CP0_Config7, MIPSCPU), +        VMSTATE_UINT64(env.lladdr, MIPSCPU), +        VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8), +        VMSTATE_INT32_ARRAY(env.CP0_WatchHi, MIPSCPU, 8), +        VMSTATE_UINTTL(env.CP0_XContext, MIPSCPU), +        VMSTATE_INT32(env.CP0_Framemask, MIPSCPU), +        VMSTATE_INT32(env.CP0_Debug, MIPSCPU), +        VMSTATE_UINTTL(env.CP0_DEPC, MIPSCPU), +        VMSTATE_INT32(env.CP0_Performance0, MIPSCPU), +        VMSTATE_UINT64(env.CP0_TagLo, MIPSCPU), +        VMSTATE_INT32(env.CP0_DataLo, MIPSCPU), +        VMSTATE_INT32(env.CP0_TagHi, MIPSCPU), +        VMSTATE_INT32(env.CP0_DataHi, MIPSCPU), +        VMSTATE_UINTTL(env.CP0_ErrorEPC, MIPSCPU), +        VMSTATE_INT32(env.CP0_DESAVE, MIPSCPU), +        VMSTATE_UINTTL_ARRAY(env.CP0_KScratch, MIPSCPU, MIPS_KSCRATCH_NUM), + +        /* Inactive TC */ +        VMSTATE_STRUCT_ARRAY(env.tcs, MIPSCPU, MIPS_SHADOW_SET_MAX, 1, +                             vmstate_inactive_tc, TCState), +        VMSTATE_STRUCT_ARRAY(env.fpus, MIPSCPU, MIPS_FPU_MAX, 1, +                             vmstate_inactive_fpu, CPUMIPSFPUContext), + +        VMSTATE_END_OF_LIST() +    }, +}; diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h new file mode 100644 index 00000000..53b185eb --- /dev/null +++ b/target-mips/mips-defs.h @@ -0,0 +1,91 @@ +#if !defined (__QEMU_MIPS_DEFS_H__) +#define __QEMU_MIPS_DEFS_H__ + +/* If we want to use host float regs... */ +//#define USE_HOST_FLOAT_REGS + +/* Real pages are variable size... */ +#define TARGET_PAGE_BITS 12 +#define MIPS_TLB_MAX 128 + +#if defined(TARGET_MIPS64) +#define TARGET_LONG_BITS 64 +#define TARGET_PHYS_ADDR_SPACE_BITS 48 +#define TARGET_VIRT_ADDR_SPACE_BITS 48 +#else +#define TARGET_LONG_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 40 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#endif + +/* Masks used to mark instructions to indicate which ISA level they +   were introduced in. */ +#define		ISA_MIPS1	0x00000001 +#define		ISA_MIPS2	0x00000002 +#define		ISA_MIPS3	0x00000004 +#define		ISA_MIPS4	0x00000008 +#define		ISA_MIPS5	0x00000010 +#define		ISA_MIPS32	0x00000020 +#define		ISA_MIPS32R2	0x00000040 +#define		ISA_MIPS64	0x00000080 +#define		ISA_MIPS64R2	0x00000100 +#define   ISA_MIPS32R3  0x00000200 +#define   ISA_MIPS64R3  0x00000400 +#define   ISA_MIPS32R5  0x00000800 +#define   ISA_MIPS64R5  0x00001000 +#define   ISA_MIPS32R6  0x00002000 +#define   ISA_MIPS64R6  0x00004000 + +/* MIPS ASEs. */ +#define   ASE_MIPS16    0x00010000 +#define   ASE_MIPS3D    0x00020000 +#define   ASE_MDMX      0x00040000 +#define   ASE_DSP       0x00080000 +#define   ASE_DSPR2     0x00100000 +#define   ASE_MT        0x00200000 +#define   ASE_SMARTMIPS 0x00400000 +#define   ASE_MICROMIPS 0x00800000 +#define   ASE_MSA       0x01000000 + +/* Chip specific instructions. */ +#define		INSN_LOONGSON2E  0x20000000 +#define		INSN_LOONGSON2F  0x40000000 +#define		INSN_VR54XX	0x80000000 + +/* MIPS CPU defines. */ +#define		CPU_MIPS1	(ISA_MIPS1) +#define		CPU_MIPS2	(CPU_MIPS1 | ISA_MIPS2) +#define		CPU_MIPS3	(CPU_MIPS2 | ISA_MIPS3) +#define		CPU_MIPS4	(CPU_MIPS3 | ISA_MIPS4) +#define		CPU_VR54XX	(CPU_MIPS4 | INSN_VR54XX) +#define		CPU_LOONGSON2E  (CPU_MIPS3 | INSN_LOONGSON2E) +#define		CPU_LOONGSON2F  (CPU_MIPS3 | INSN_LOONGSON2F) + +#define		CPU_MIPS5	(CPU_MIPS4 | ISA_MIPS5) + +/* MIPS Technologies "Release 1" */ +#define		CPU_MIPS32	(CPU_MIPS2 | ISA_MIPS32) +#define		CPU_MIPS64	(CPU_MIPS5 | CPU_MIPS32 | ISA_MIPS64) + +/* MIPS Technologies "Release 2" */ +#define		CPU_MIPS32R2	(CPU_MIPS32 | ISA_MIPS32R2) +#define		CPU_MIPS64R2	(CPU_MIPS64 | CPU_MIPS32R2 | ISA_MIPS64R2) + +/* MIPS Technologies "Release 3" */ +#define CPU_MIPS32R3 (CPU_MIPS32R2 | ISA_MIPS32R3) +#define CPU_MIPS64R3 (CPU_MIPS64R2 | CPU_MIPS32R3 | ISA_MIPS64R3) + +/* MIPS Technologies "Release 5" */ +#define CPU_MIPS32R5 (CPU_MIPS32R3 | ISA_MIPS32R5) +#define CPU_MIPS64R5 (CPU_MIPS64R3 | CPU_MIPS32R5 | ISA_MIPS64R5) + +/* MIPS Technologies "Release 6" */ +#define CPU_MIPS32R6 (CPU_MIPS32R5 | ISA_MIPS32R6) +#define CPU_MIPS64R6 (CPU_MIPS64R5 | CPU_MIPS32R6 | ISA_MIPS64R6) + +/* Strictly follow the architecture standard: +   - Disallow "special" instruction handling for PMON/SPIM. +   Note that we still maintain Count/Compare to match the host clock. */ +//#define MIPS_STRICT_STANDARD 1 + +#endif /* !defined (__QEMU_MIPS_DEFS_H__) */ diff --git a/target-mips/mips-semi.c b/target-mips/mips-semi.c new file mode 100644 index 00000000..5050940c --- /dev/null +++ b/target-mips/mips-semi.c @@ -0,0 +1,373 @@ +/* + * Unified Hosting Interface syscalls. + * + * Copyright (c) 2015 Imagination Technologies + * + * 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 <sys/stat.h> +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/softmmu-semi.h" +#include "exec/semihost.h" + +typedef enum UHIOp { +    UHI_exit = 1, +    UHI_open = 2, +    UHI_close = 3, +    UHI_read = 4, +    UHI_write = 5, +    UHI_lseek = 6, +    UHI_unlink = 7, +    UHI_fstat = 8, +    UHI_argc = 9, +    UHI_argnlen = 10, +    UHI_argn = 11, +    UHI_plog = 13, +    UHI_assert = 14, +    UHI_pread = 19, +    UHI_pwrite = 20, +    UHI_link = 22 +} UHIOp; + +typedef struct UHIStat { +    int16_t uhi_st_dev; +    uint16_t uhi_st_ino; +    uint32_t uhi_st_mode; +    uint16_t uhi_st_nlink; +    uint16_t uhi_st_uid; +    uint16_t uhi_st_gid; +    int16_t uhi_st_rdev; +    uint64_t uhi_st_size; +    uint64_t uhi_st_atime; +    uint64_t uhi_st_spare1; +    uint64_t uhi_st_mtime; +    uint64_t uhi_st_spare2; +    uint64_t uhi_st_ctime; +    uint64_t uhi_st_spare3; +    uint64_t uhi_st_blksize; +    uint64_t uhi_st_blocks; +    uint64_t uhi_st_spare4[2]; +} UHIStat; + +enum UHIOpenFlags { +    UHIOpen_RDONLY = 0x0, +    UHIOpen_WRONLY = 0x1, +    UHIOpen_RDWR   = 0x2, +    UHIOpen_APPEND = 0x8, +    UHIOpen_CREAT  = 0x200, +    UHIOpen_TRUNC  = 0x400, +    UHIOpen_EXCL   = 0x800 +}; + +/* Errno values taken from asm-mips/errno.h */ +static uint16_t host_to_mips_errno[] = { +    [ENAMETOOLONG] = 78, +#ifdef EOVERFLOW +    [EOVERFLOW]    = 79, +#endif +#ifdef ELOOP +    [ELOOP]        = 90, +#endif +}; + +static int errno_mips(int err) +{ +    if (err < 0 || err >= ARRAY_SIZE(host_to_mips_errno)) { +        return EINVAL; +    } else if (host_to_mips_errno[err]) { +        return host_to_mips_errno[err]; +    } else { +        return err; +    } +} + +static int copy_stat_to_target(CPUMIPSState *env, const struct stat *src, +                               target_ulong vaddr) +{ +    hwaddr len = sizeof(struct UHIStat); +    UHIStat *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); +    if (!dst) { +        errno = EFAULT; +        return -1; +    } + +    dst->uhi_st_dev = tswap16(src->st_dev); +    dst->uhi_st_ino = tswap16(src->st_ino); +    dst->uhi_st_mode = tswap32(src->st_mode); +    dst->uhi_st_nlink = tswap16(src->st_nlink); +    dst->uhi_st_uid = tswap16(src->st_uid); +    dst->uhi_st_gid = tswap16(src->st_gid); +    dst->uhi_st_rdev = tswap16(src->st_rdev); +    dst->uhi_st_size = tswap64(src->st_size); +    dst->uhi_st_atime = tswap64(src->st_atime); +    dst->uhi_st_mtime = tswap64(src->st_mtime); +    dst->uhi_st_ctime = tswap64(src->st_ctime); +#ifdef _WIN32 +    dst->uhi_st_blksize = 0; +    dst->uhi_st_blocks = 0; +#else +    dst->uhi_st_blksize = tswap64(src->st_blksize); +    dst->uhi_st_blocks = tswap64(src->st_blocks); +#endif +    unlock_user(dst, vaddr, len); +    return 0; +} + +static int get_open_flags(target_ulong target_flags) +{ +    int open_flags = 0; + +    if (target_flags & UHIOpen_RDWR) { +        open_flags |= O_RDWR; +    } else if (target_flags & UHIOpen_WRONLY) { +        open_flags |= O_WRONLY; +    } else { +        open_flags |= O_RDONLY; +    } + +    open_flags |= (target_flags & UHIOpen_APPEND) ? O_APPEND : 0; +    open_flags |= (target_flags & UHIOpen_CREAT)  ? O_CREAT  : 0; +    open_flags |= (target_flags & UHIOpen_TRUNC)  ? O_TRUNC  : 0; +    open_flags |= (target_flags & UHIOpen_EXCL)   ? O_EXCL   : 0; + +    return open_flags; +} + +static int write_to_file(CPUMIPSState *env, target_ulong fd, target_ulong vaddr, +                         target_ulong len, target_ulong offset) +{ +    int num_of_bytes; +    void *dst = lock_user(VERIFY_READ, vaddr, len, 1); +    if (!dst) { +        errno = EFAULT; +        return -1; +    } + +    if (offset) { +#ifdef _WIN32 +        num_of_bytes = 0; +#else +        num_of_bytes = pwrite(fd, dst, len, offset); +#endif +    } else { +        num_of_bytes = write(fd, dst, len); +    } + +    unlock_user(dst, vaddr, 0); +    return num_of_bytes; +} + +static int read_from_file(CPUMIPSState *env, target_ulong fd, +                          target_ulong vaddr, target_ulong len, +                          target_ulong offset) +{ +    int num_of_bytes; +    void *dst = lock_user(VERIFY_WRITE, vaddr, len, 0); +    if (!dst) { +        errno = EFAULT; +        return -1; +    } + +    if (offset) { +#ifdef _WIN32 +        num_of_bytes = 0; +#else +        num_of_bytes = pread(fd, dst, len, offset); +#endif +    } else { +        num_of_bytes = read(fd, dst, len); +    } + +    unlock_user(dst, vaddr, len); +    return num_of_bytes; +} + +static int copy_argn_to_target(CPUMIPSState *env, int arg_num, +                               target_ulong vaddr) +{ +    int strsize = strlen(semihosting_get_arg(arg_num)) + 1; +    char *dst = lock_user(VERIFY_WRITE, vaddr, strsize, 0); +    if (!dst) { +        return -1; +    } + +    strcpy(dst, semihosting_get_arg(arg_num)); + +    unlock_user(dst, vaddr, strsize); +    return 0; +} + +#define GET_TARGET_STRING(p, addr)              \ +    do {                                        \ +        p = lock_user_string(addr);             \ +        if (!p) {                               \ +            gpr[2] = -1;                        \ +            gpr[3] = EFAULT;                    \ +            goto uhi_done;                      \ +        }                                       \ +    } while (0) + +#define GET_TARGET_STRINGS_2(p, addr, p2, addr2)        \ +    do {                                                \ +        p = lock_user_string(addr);                     \ +        if (!p) {                                       \ +            gpr[2] = -1;                                \ +            gpr[3] = EFAULT;                            \ +            goto uhi_done;                              \ +        }                                               \ +        p2 = lock_user_string(addr2);                   \ +        if (!p2) {                                      \ +            unlock_user(p, addr, 0);                    \ +            gpr[2] = -1;                                \ +            gpr[3] = EFAULT;                            \ +            goto uhi_done;                              \ +        }                                               \ +    } while (0) + +#define FREE_TARGET_STRING(p, gpr)              \ +    do {                                        \ +        unlock_user(p, gpr, 0);                 \ +    } while (0) + +void helper_do_semihosting(CPUMIPSState *env) +{ +    target_ulong *gpr = env->active_tc.gpr; +    const UHIOp op = gpr[25]; +    char *p, *p2; + +    switch (op) { +    case UHI_exit: +        qemu_log("UHI(%d): exit(%d)\n", op, (int)gpr[4]); +        exit(gpr[4]); +    case UHI_open: +        GET_TARGET_STRING(p, gpr[4]); +        if (!strcmp("/dev/stdin", p)) { +            gpr[2] = 0; +        } else if (!strcmp("/dev/stdout", p)) { +            gpr[2] = 1; +        } else if (!strcmp("/dev/stderr", p)) { +            gpr[2] = 2; +        } else { +            gpr[2] = open(p, get_open_flags(gpr[5]), gpr[6]); +            gpr[3] = errno_mips(errno); +        } +        FREE_TARGET_STRING(p, gpr[4]); +        break; +    case UHI_close: +        if (gpr[4] < 3) { +            /* ignore closing stdin/stdout/stderr */ +            gpr[2] = 0; +            goto uhi_done; +        } +        gpr[2] = close(gpr[4]); +        gpr[3] = errno_mips(errno); +        break; +    case UHI_read: +        gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], 0); +        gpr[3] = errno_mips(errno); +        break; +    case UHI_write: +        gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], 0); +        gpr[3] = errno_mips(errno); +        break; +    case UHI_lseek: +        gpr[2] = lseek(gpr[4], gpr[5], gpr[6]); +        gpr[3] = errno_mips(errno); +        break; +    case UHI_unlink: +        GET_TARGET_STRING(p, gpr[4]); +        gpr[2] = remove(p); +        gpr[3] = errno_mips(errno); +        FREE_TARGET_STRING(p, gpr[4]); +        break; +    case UHI_fstat: +        { +            struct stat sbuf; +            memset(&sbuf, 0, sizeof(sbuf)); +            gpr[2] = fstat(gpr[4], &sbuf); +            gpr[3] = errno_mips(errno); +            if (gpr[2]) { +                goto uhi_done; +            } +            gpr[2] = copy_stat_to_target(env, &sbuf, gpr[5]); +            gpr[3] = errno_mips(errno); +        } +        break; +    case UHI_argc: +        gpr[2] = semihosting_get_argc(); +        break; +    case UHI_argnlen: +        if (gpr[4] >= semihosting_get_argc()) { +            gpr[2] = -1; +            goto uhi_done; +        } +        gpr[2] = strlen(semihosting_get_arg(gpr[4])); +        break; +    case UHI_argn: +        if (gpr[4] >= semihosting_get_argc()) { +            gpr[2] = -1; +            goto uhi_done; +        } +        gpr[2] = copy_argn_to_target(env, gpr[4], gpr[5]); +        break; +    case UHI_plog: +        GET_TARGET_STRING(p, gpr[4]); +        p2 = strstr(p, "%d"); +        if (p2) { +            int char_num = p2 - p; +            char *buf = g_malloc(char_num + 1); +            strncpy(buf, p, char_num); +            buf[char_num] = '\0'; +            gpr[2] = printf("%s%d%s", buf, (int)gpr[5], p2 + 2); +            g_free(buf); +        } else { +            gpr[2] = printf("%s", p); +        } +        FREE_TARGET_STRING(p, gpr[4]); +        break; +    case UHI_assert: +        GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); +        printf("assertion '"); +        printf("\"%s\"", p); +        printf("': file \"%s\", line %d\n", p2, (int)gpr[6]); +        FREE_TARGET_STRING(p2, gpr[5]); +        FREE_TARGET_STRING(p, gpr[4]); +        abort(); +        break; +    case UHI_pread: +        gpr[2] = read_from_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); +        gpr[3] = errno_mips(errno); +        break; +    case UHI_pwrite: +        gpr[2] = write_to_file(env, gpr[4], gpr[5], gpr[6], gpr[7]); +        gpr[3] = errno_mips(errno); +        break; +#ifndef _WIN32 +    case UHI_link: +        GET_TARGET_STRINGS_2(p, gpr[4], p2, gpr[5]); +        gpr[2] = link(p, p2); +        gpr[3] = errno_mips(errno); +        FREE_TARGET_STRING(p2, gpr[5]); +        FREE_TARGET_STRING(p, gpr[4]); +        break; +#endif +    default: +        fprintf(stderr, "Unknown UHI operation %d\n", op); +        abort(); +    } +uhi_done: +    return; +} diff --git a/target-mips/msa_helper.c b/target-mips/msa_helper.c new file mode 100644 index 00000000..a1cb48f2 --- /dev/null +++ b/target-mips/msa_helper.c @@ -0,0 +1,3433 @@ +/* + * MIPS SIMD Architecture Module Instruction emulation helpers for QEMU. + * + * Copyright (c) 2014 Imagination Technologies + * + * 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 "cpu.h" +#include "exec/helper-proto.h" + +/* Data format min and max values */ +#define DF_BITS(df) (1 << ((df) + 3)) + +#define DF_MAX_INT(df)  (int64_t)((1LL << (DF_BITS(df) - 1)) - 1) +#define M_MAX_INT(m)    (int64_t)((1LL << ((m)         - 1)) - 1) + +#define DF_MIN_INT(df)  (int64_t)(-(1LL << (DF_BITS(df) - 1))) +#define M_MIN_INT(m)    (int64_t)(-(1LL << ((m)         - 1))) + +#define DF_MAX_UINT(df) (uint64_t)(-1ULL >> (64 - DF_BITS(df))) +#define M_MAX_UINT(m)   (uint64_t)(-1ULL >> (64 - (m))) + +#define UNSIGNED(x, df) ((x) & DF_MAX_UINT(df)) +#define SIGNED(x, df)                                                   \ +    ((((int64_t)x) << (64 - DF_BITS(df))) >> (64 - DF_BITS(df))) + +/* Element-by-element access macros */ +#define DF_ELEMENTS(df) (MSA_WRLEN / DF_BITS(df)) + +static inline void msa_move_v(wr_t *pwd, wr_t *pws) +{ +    uint32_t i; + +    for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +        pwd->d[i] = pws->d[i]; +    } +} + +#define MSA_FN_IMM8(FUNC, DEST, OPERATION)                              \ +void helper_msa_ ## FUNC(CPUMIPSState *env, uint32_t wd, uint32_t ws,   \ +        uint32_t i8)                                                    \ +{                                                                       \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                          \ +    wr_t *pws = &(env->active_fpu.fpr[ws].wr);                          \ +    uint32_t i;                                                         \ +    for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) {                        \ +        DEST = OPERATION;                                               \ +    }                                                                   \ +} + +MSA_FN_IMM8(andi_b, pwd->b[i], pws->b[i] & i8) +MSA_FN_IMM8(ori_b, pwd->b[i], pws->b[i] | i8) +MSA_FN_IMM8(nori_b, pwd->b[i], ~(pws->b[i] | i8)) +MSA_FN_IMM8(xori_b, pwd->b[i], pws->b[i] ^ i8) + +#define BIT_MOVE_IF_NOT_ZERO(dest, arg1, arg2, df) \ +            UNSIGNED(((dest & (~arg2)) | (arg1 & arg2)), df) +MSA_FN_IMM8(bmnzi_b, pwd->b[i], +        BIT_MOVE_IF_NOT_ZERO(pwd->b[i], pws->b[i], i8, DF_BYTE)) + +#define BIT_MOVE_IF_ZERO(dest, arg1, arg2, df) \ +            UNSIGNED((dest & arg2) | (arg1 & (~arg2)), df) +MSA_FN_IMM8(bmzi_b, pwd->b[i], +        BIT_MOVE_IF_ZERO(pwd->b[i], pws->b[i], i8, DF_BYTE)) + +#define BIT_SELECT(dest, arg1, arg2, df) \ +            UNSIGNED((arg1 & (~dest)) | (arg2 & dest), df) +MSA_FN_IMM8(bseli_b, pwd->b[i], +        BIT_SELECT(pwd->b[i], pws->b[i], i8, DF_BYTE)) + +#undef MSA_FN_IMM8 + +#define SHF_POS(i, imm) (((i) & 0xfc) + (((imm) >> (2 * ((i) & 0x03))) & 0x03)) + +void helper_msa_shf_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                       uint32_t ws, uint32_t imm) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    switch (df) { +    case DF_BYTE: +        for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { +            pwx->b[i] = pws->b[SHF_POS(i, imm)]; +        } +        break; +    case DF_HALF: +        for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { +            pwx->h[i] = pws->h[SHF_POS(i, imm)]; +        } +        break; +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            pwx->w[i] = pws->w[SHF_POS(i, imm)]; +        } +        break; +    default: +        assert(0); +    } +    msa_move_v(pwd, pwx); +} + +#define MSA_FN_VECTOR(FUNC, DEST, OPERATION)                            \ +void helper_msa_ ## FUNC(CPUMIPSState *env, uint32_t wd, uint32_t ws,   \ +        uint32_t wt)                                                    \ +{                                                                       \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                          \ +    wr_t *pws = &(env->active_fpu.fpr[ws].wr);                          \ +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr);                          \ +    uint32_t i;                                                         \ +    for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) {                      \ +        DEST = OPERATION;                                               \ +    }                                                                   \ +} + +MSA_FN_VECTOR(and_v, pwd->d[i], pws->d[i] & pwt->d[i]) +MSA_FN_VECTOR(or_v, pwd->d[i], pws->d[i] | pwt->d[i]) +MSA_FN_VECTOR(nor_v, pwd->d[i], ~(pws->d[i] | pwt->d[i])) +MSA_FN_VECTOR(xor_v, pwd->d[i], pws->d[i] ^ pwt->d[i]) +MSA_FN_VECTOR(bmnz_v, pwd->d[i], +        BIT_MOVE_IF_NOT_ZERO(pwd->d[i], pws->d[i], pwt->d[i], DF_DOUBLE)) +MSA_FN_VECTOR(bmz_v, pwd->d[i], +        BIT_MOVE_IF_ZERO(pwd->d[i], pws->d[i], pwt->d[i], DF_DOUBLE)) +MSA_FN_VECTOR(bsel_v, pwd->d[i], +        BIT_SELECT(pwd->d[i], pws->d[i], pwt->d[i], DF_DOUBLE)) +#undef BIT_MOVE_IF_NOT_ZERO +#undef BIT_MOVE_IF_ZERO +#undef BIT_SELECT +#undef MSA_FN_VECTOR + +static inline int64_t msa_addv_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return arg1 + arg2; +} + +static inline int64_t msa_subv_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return arg1 - arg2; +} + +static inline int64_t msa_ceq_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return arg1 == arg2 ? -1 : 0; +} + +static inline int64_t msa_cle_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return arg1 <= arg2 ? -1 : 0; +} + +static inline int64_t msa_cle_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    return u_arg1 <= u_arg2 ? -1 : 0; +} + +static inline int64_t msa_clt_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return arg1 < arg2 ? -1 : 0; +} + +static inline int64_t msa_clt_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    return u_arg1 < u_arg2 ? -1 : 0; +} + +static inline int64_t msa_max_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return arg1 > arg2 ? arg1 : arg2; +} + +static inline int64_t msa_max_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    return u_arg1 > u_arg2 ? arg1 : arg2; +} + +static inline int64_t msa_min_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return arg1 < arg2 ? arg1 : arg2; +} + +static inline int64_t msa_min_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    return u_arg1 < u_arg2 ? arg1 : arg2; +} + +#define MSA_BINOP_IMM_DF(helper, func)                                  \ +void helper_msa_ ## helper ## _df(CPUMIPSState *env, uint32_t df,       \ +                        uint32_t wd, uint32_t ws, int32_t u5)           \ +{                                                                       \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                          \ +    wr_t *pws = &(env->active_fpu.fpr[ws].wr);                          \ +    uint32_t i;                                                         \ +                                                                        \ +    switch (df) {                                                       \ +    case DF_BYTE:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) {                    \ +            pwd->b[i] = msa_ ## func ## _df(df, pws->b[i], u5);         \ +        }                                                               \ +        break;                                                          \ +    case DF_HALF:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) {                    \ +            pwd->h[i] = msa_ ## func ## _df(df, pws->h[i], u5);         \ +        }                                                               \ +        break;                                                          \ +    case DF_WORD:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) {                    \ +            pwd->w[i] = msa_ ## func ## _df(df, pws->w[i], u5);         \ +        }                                                               \ +        break;                                                          \ +    case DF_DOUBLE:                                                     \ +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) {                  \ +            pwd->d[i] = msa_ ## func ## _df(df, pws->d[i], u5);         \ +        }                                                               \ +        break;                                                          \ +    default:                                                            \ +        assert(0);                                                      \ +    }                                                                   \ +} + +MSA_BINOP_IMM_DF(addvi, addv) +MSA_BINOP_IMM_DF(subvi, subv) +MSA_BINOP_IMM_DF(ceqi, ceq) +MSA_BINOP_IMM_DF(clei_s, cle_s) +MSA_BINOP_IMM_DF(clei_u, cle_u) +MSA_BINOP_IMM_DF(clti_s, clt_s) +MSA_BINOP_IMM_DF(clti_u, clt_u) +MSA_BINOP_IMM_DF(maxi_s, max_s) +MSA_BINOP_IMM_DF(maxi_u, max_u) +MSA_BINOP_IMM_DF(mini_s, min_s) +MSA_BINOP_IMM_DF(mini_u, min_u) +#undef MSA_BINOP_IMM_DF + +void helper_msa_ldi_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                       int32_t s10) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    uint32_t i; + +    switch (df) { +    case DF_BYTE: +        for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { +            pwd->b[i] = (int8_t)s10; +        } +        break; +    case DF_HALF: +        for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { +            pwd->h[i] = (int16_t)s10; +        } +        break; +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            pwd->w[i] = (int32_t)s10; +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            pwd->d[i] = (int64_t)s10; +        } +       break; +    default: +        assert(0); +    } +} + +/* Data format bit position and unsigned values */ +#define BIT_POSITION(x, df) ((uint64_t)(x) % DF_BITS(df)) + +static inline int64_t msa_sll_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int32_t b_arg2 = BIT_POSITION(arg2, df); +    return arg1 << b_arg2; +} + +static inline int64_t msa_sra_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int32_t b_arg2 = BIT_POSITION(arg2, df); +    return arg1 >> b_arg2; +} + +static inline int64_t msa_srl_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    int32_t b_arg2 = BIT_POSITION(arg2, df); +    return u_arg1 >> b_arg2; +} + +static inline int64_t msa_bclr_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int32_t b_arg2 = BIT_POSITION(arg2, df); +    return UNSIGNED(arg1 & (~(1LL << b_arg2)), df); +} + +static inline int64_t msa_bset_df(uint32_t df, int64_t arg1, +        int64_t arg2) +{ +    int32_t b_arg2 = BIT_POSITION(arg2, df); +    return UNSIGNED(arg1 | (1LL << b_arg2), df); +} + +static inline int64_t msa_bneg_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int32_t b_arg2 = BIT_POSITION(arg2, df); +    return UNSIGNED(arg1 ^ (1LL << b_arg2), df); +} + +static inline int64_t msa_binsl_df(uint32_t df, int64_t dest, int64_t arg1, +                                   int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_dest = UNSIGNED(dest, df); +    int32_t sh_d = BIT_POSITION(arg2, df) + 1; +    int32_t sh_a = DF_BITS(df) - sh_d; +    if (sh_d == DF_BITS(df)) { +        return u_arg1; +    } else { +        return UNSIGNED(UNSIGNED(u_dest << sh_d, df) >> sh_d, df) | +               UNSIGNED(UNSIGNED(u_arg1 >> sh_a, df) << sh_a, df); +    } +} + +static inline int64_t msa_binsr_df(uint32_t df, int64_t dest, int64_t arg1, +                                   int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_dest = UNSIGNED(dest, df); +    int32_t sh_d = BIT_POSITION(arg2, df) + 1; +    int32_t sh_a = DF_BITS(df) - sh_d; +    if (sh_d == DF_BITS(df)) { +        return u_arg1; +    } else { +        return UNSIGNED(UNSIGNED(u_dest >> sh_d, df) << sh_d, df) | +               UNSIGNED(UNSIGNED(u_arg1 << sh_a, df) >> sh_a, df); +    } +} + +static inline int64_t msa_sat_s_df(uint32_t df, int64_t arg, uint32_t m) +{ +    return arg < M_MIN_INT(m+1) ? M_MIN_INT(m+1) : +                                  arg > M_MAX_INT(m+1) ? M_MAX_INT(m+1) : +                                                         arg; +} + +static inline int64_t msa_sat_u_df(uint32_t df, int64_t arg, uint32_t m) +{ +    uint64_t u_arg = UNSIGNED(arg, df); +    return  u_arg < M_MAX_UINT(m+1) ? u_arg : +                                      M_MAX_UINT(m+1); +} + +static inline int64_t msa_srar_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int32_t b_arg2 = BIT_POSITION(arg2, df); +    if (b_arg2 == 0) { +        return arg1; +    } else { +        int64_t r_bit = (arg1 >> (b_arg2 - 1)) & 1; +        return (arg1 >> b_arg2) + r_bit; +    } +} + +static inline int64_t msa_srlr_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    int32_t b_arg2 = BIT_POSITION(arg2, df); +    if (b_arg2 == 0) { +        return u_arg1; +    } else { +        uint64_t r_bit = (u_arg1 >> (b_arg2 - 1)) & 1; +        return (u_arg1 >> b_arg2) + r_bit; +    } +} + +#define MSA_BINOP_IMMU_DF(helper, func)                                  \ +void helper_msa_ ## helper ## _df(CPUMIPSState *env, uint32_t df, uint32_t wd, \ +                       uint32_t ws, uint32_t u5)                        \ +{                                                                       \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                          \ +    wr_t *pws = &(env->active_fpu.fpr[ws].wr);                          \ +    uint32_t i;                                                         \ +                                                                        \ +    switch (df) {                                                       \ +    case DF_BYTE:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) {                    \ +            pwd->b[i] = msa_ ## func ## _df(df, pws->b[i], u5);         \ +        }                                                               \ +        break;                                                          \ +    case DF_HALF:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) {                    \ +            pwd->h[i] = msa_ ## func ## _df(df, pws->h[i], u5);         \ +        }                                                               \ +        break;                                                          \ +    case DF_WORD:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) {                    \ +            pwd->w[i] = msa_ ## func ## _df(df, pws->w[i], u5);         \ +        }                                                               \ +        break;                                                          \ +    case DF_DOUBLE:                                                     \ +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) {                  \ +            pwd->d[i] = msa_ ## func ## _df(df, pws->d[i], u5);         \ +        }                                                               \ +        break;                                                          \ +    default:                                                            \ +        assert(0);                                                      \ +    }                                                                   \ +} + +MSA_BINOP_IMMU_DF(slli, sll) +MSA_BINOP_IMMU_DF(srai, sra) +MSA_BINOP_IMMU_DF(srli, srl) +MSA_BINOP_IMMU_DF(bclri, bclr) +MSA_BINOP_IMMU_DF(bseti, bset) +MSA_BINOP_IMMU_DF(bnegi, bneg) +MSA_BINOP_IMMU_DF(sat_s, sat_s) +MSA_BINOP_IMMU_DF(sat_u, sat_u) +MSA_BINOP_IMMU_DF(srari, srar) +MSA_BINOP_IMMU_DF(srlri, srlr) +#undef MSA_BINOP_IMMU_DF + +#define MSA_TEROP_IMMU_DF(helper, func)                                  \ +void helper_msa_ ## helper ## _df(CPUMIPSState *env, uint32_t df,       \ +                                  uint32_t wd, uint32_t ws, uint32_t u5) \ +{                                                                       \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                          \ +    wr_t *pws = &(env->active_fpu.fpr[ws].wr);                          \ +    uint32_t i;                                                         \ +                                                                        \ +    switch (df) {                                                       \ +    case DF_BYTE:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) {                    \ +            pwd->b[i] = msa_ ## func ## _df(df, pwd->b[i], pws->b[i],   \ +                                            u5);                        \ +        }                                                               \ +        break;                                                          \ +    case DF_HALF:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) {                    \ +            pwd->h[i] = msa_ ## func ## _df(df, pwd->h[i], pws->h[i],   \ +                                            u5);                        \ +        }                                                               \ +        break;                                                          \ +    case DF_WORD:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) {                    \ +            pwd->w[i] = msa_ ## func ## _df(df, pwd->w[i], pws->w[i],   \ +                                            u5);                        \ +        }                                                               \ +        break;                                                          \ +    case DF_DOUBLE:                                                     \ +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) {                  \ +            pwd->d[i] = msa_ ## func ## _df(df, pwd->d[i], pws->d[i],   \ +                                            u5);                        \ +        }                                                               \ +        break;                                                          \ +    default:                                                            \ +        assert(0);                                                      \ +    }                                                                   \ +} + +MSA_TEROP_IMMU_DF(binsli, binsl) +MSA_TEROP_IMMU_DF(binsri, binsr) +#undef MSA_TEROP_IMMU_DF + +static inline int64_t msa_max_a_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t abs_arg1 = arg1 >= 0 ? arg1 : -arg1; +    uint64_t abs_arg2 = arg2 >= 0 ? arg2 : -arg2; +    return abs_arg1 > abs_arg2 ? arg1 : arg2; +} + +static inline int64_t msa_min_a_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t abs_arg1 = arg1 >= 0 ? arg1 : -arg1; +    uint64_t abs_arg2 = arg2 >= 0 ? arg2 : -arg2; +    return abs_arg1 < abs_arg2 ? arg1 : arg2; +} + +static inline int64_t msa_add_a_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t abs_arg1 = arg1 >= 0 ? arg1 : -arg1; +    uint64_t abs_arg2 = arg2 >= 0 ? arg2 : -arg2; +    return abs_arg1 + abs_arg2; +} + +static inline int64_t msa_adds_a_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t max_int = (uint64_t)DF_MAX_INT(df); +    uint64_t abs_arg1 = arg1 >= 0 ? arg1 : -arg1; +    uint64_t abs_arg2 = arg2 >= 0 ? arg2 : -arg2; +    if (abs_arg1 > max_int || abs_arg2 > max_int) { +        return (int64_t)max_int; +    } else { +        return (abs_arg1 < max_int - abs_arg2) ? abs_arg1 + abs_arg2 : max_int; +    } +} + +static inline int64_t msa_adds_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int64_t max_int = DF_MAX_INT(df); +    int64_t min_int = DF_MIN_INT(df); +    if (arg1 < 0) { +        return (min_int - arg1 < arg2) ? arg1 + arg2 : min_int; +    } else { +        return (arg2 < max_int - arg1) ? arg1 + arg2 : max_int; +    } +} + +static inline uint64_t msa_adds_u_df(uint32_t df, uint64_t arg1, uint64_t arg2) +{ +    uint64_t max_uint = DF_MAX_UINT(df); +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    return (u_arg1 < max_uint - u_arg2) ? u_arg1 + u_arg2 : max_uint; +} + +static inline int64_t msa_ave_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    /* signed shift */ +    return (arg1 >> 1) + (arg2 >> 1) + (arg1 & arg2 & 1); +} + +static inline uint64_t msa_ave_u_df(uint32_t df, uint64_t arg1, uint64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    /* unsigned shift */ +    return (u_arg1 >> 1) + (u_arg2 >> 1) + (u_arg1 & u_arg2 & 1); +} + +static inline int64_t msa_aver_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    /* signed shift */ +    return (arg1 >> 1) + (arg2 >> 1) + ((arg1 | arg2) & 1); +} + +static inline uint64_t msa_aver_u_df(uint32_t df, uint64_t arg1, uint64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    /* unsigned shift */ +    return (u_arg1 >> 1) + (u_arg2 >> 1) + ((u_arg1 | u_arg2) & 1); +} + +static inline int64_t msa_subs_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int64_t max_int = DF_MAX_INT(df); +    int64_t min_int = DF_MIN_INT(df); +    if (arg2 > 0) { +        return (min_int + arg2 < arg1) ? arg1 - arg2 : min_int; +    } else { +        return (arg1 < max_int + arg2) ? arg1 - arg2 : max_int; +    } +} + +static inline int64_t msa_subs_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    return (u_arg1 > u_arg2) ? u_arg1 - u_arg2 : 0; +} + +static inline int64_t msa_subsus_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t max_uint = DF_MAX_UINT(df); +    if (arg2 >= 0) { +        uint64_t u_arg2 = (uint64_t)arg2; +        return (u_arg1 > u_arg2) ? +            (int64_t)(u_arg1 - u_arg2) : +            0; +    } else { +        uint64_t u_arg2 = (uint64_t)(-arg2); +        return (u_arg1 < max_uint - u_arg2) ? +            (int64_t)(u_arg1 + u_arg2) : +            (int64_t)max_uint; +    } +} + +static inline int64_t msa_subsuu_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    int64_t max_int = DF_MAX_INT(df); +    int64_t min_int = DF_MIN_INT(df); +    if (u_arg1 > u_arg2) { +        return u_arg1 - u_arg2 < (uint64_t)max_int ? +            (int64_t)(u_arg1 - u_arg2) : +            max_int; +    } else { +        return u_arg2 - u_arg1 < (uint64_t)(-min_int) ? +            (int64_t)(u_arg1 - u_arg2) : +            min_int; +    } +} + +static inline int64_t msa_asub_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    /* signed compare */ +    return (arg1 < arg2) ? +        (uint64_t)(arg2 - arg1) : (uint64_t)(arg1 - arg2); +} + +static inline uint64_t msa_asub_u_df(uint32_t df, uint64_t arg1, uint64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    /* unsigned compare */ +    return (u_arg1 < u_arg2) ? +        (uint64_t)(u_arg2 - u_arg1) : (uint64_t)(u_arg1 - u_arg2); +} + +static inline int64_t msa_mulv_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return arg1 * arg2; +} + +static inline int64_t msa_div_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    if (arg1 == DF_MIN_INT(df) && arg2 == -1) { +        return DF_MIN_INT(df); +    } +    return arg2 ? arg1 / arg2 : 0; +} + +static inline int64_t msa_div_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    return u_arg2 ? u_arg1 / u_arg2 : 0; +} + +static inline int64_t msa_mod_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    if (arg1 == DF_MIN_INT(df) && arg2 == -1) { +        return 0; +    } +    return arg2 ? arg1 % arg2 : 0; +} + +static inline int64_t msa_mod_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    uint64_t u_arg1 = UNSIGNED(arg1, df); +    uint64_t u_arg2 = UNSIGNED(arg2, df); +    return u_arg2 ? u_arg1 % u_arg2 : 0; +} + +#define SIGNED_EVEN(a, df) \ +        ((((int64_t)(a)) << (64 - DF_BITS(df)/2)) >> (64 - DF_BITS(df)/2)) + +#define UNSIGNED_EVEN(a, df) \ +        ((((uint64_t)(a)) << (64 - DF_BITS(df)/2)) >> (64 - DF_BITS(df)/2)) + +#define SIGNED_ODD(a, df) \ +        ((((int64_t)(a)) << (64 - DF_BITS(df))) >> (64 - DF_BITS(df)/2)) + +#define UNSIGNED_ODD(a, df) \ +        ((((uint64_t)(a)) << (64 - DF_BITS(df))) >> (64 - DF_BITS(df)/2)) + +#define SIGNED_EXTRACT(e, o, a, df)     \ +    do {                                \ +        e = SIGNED_EVEN(a, df);         \ +        o = SIGNED_ODD(a, df);          \ +    } while (0); + +#define UNSIGNED_EXTRACT(e, o, a, df)   \ +    do {                                \ +        e = UNSIGNED_EVEN(a, df);       \ +        o = UNSIGNED_ODD(a, df);        \ +    } while (0); + +static inline int64_t msa_dotp_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int64_t even_arg1; +    int64_t even_arg2; +    int64_t odd_arg1; +    int64_t odd_arg2; +    SIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); +    SIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); +    return (even_arg1 * even_arg2) + (odd_arg1 * odd_arg2); +} + +static inline int64_t msa_dotp_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int64_t even_arg1; +    int64_t even_arg2; +    int64_t odd_arg1; +    int64_t odd_arg2; +    UNSIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); +    UNSIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); +    return (even_arg1 * even_arg2) + (odd_arg1 * odd_arg2); +} + +#define CONCATENATE_AND_SLIDE(s, k)             \ +    do {                                        \ +        for (i = 0; i < s; i++) {               \ +            v[i]     = pws->b[s * k + i];       \ +            v[i + s] = pwd->b[s * k + i];       \ +        }                                       \ +        for (i = 0; i < s; i++) {               \ +            pwd->b[s * k + i] = v[i + n];       \ +        }                                       \ +    } while (0) + +static inline void msa_sld_df(uint32_t df, wr_t *pwd, +                              wr_t *pws, target_ulong rt) +{ +    uint32_t n = rt % DF_ELEMENTS(df); +    uint8_t v[64]; +    uint32_t i, k; + +    switch (df) { +    case DF_BYTE: +        CONCATENATE_AND_SLIDE(DF_ELEMENTS(DF_BYTE), 0); +        break; +    case DF_HALF: +        for (k = 0; k < 2; k++) { +            CONCATENATE_AND_SLIDE(DF_ELEMENTS(DF_HALF), k); +        } +        break; +    case DF_WORD: +        for (k = 0; k < 4; k++) { +            CONCATENATE_AND_SLIDE(DF_ELEMENTS(DF_WORD), k); +        } +        break; +    case DF_DOUBLE: +        for (k = 0; k < 8; k++) { +            CONCATENATE_AND_SLIDE(DF_ELEMENTS(DF_DOUBLE), k); +        } +        break; +    default: +        assert(0); +    } +} + +static inline int64_t msa_hadd_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return SIGNED_ODD(arg1, df) + SIGNED_EVEN(arg2, df); +} + +static inline int64_t msa_hadd_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return UNSIGNED_ODD(arg1, df) + UNSIGNED_EVEN(arg2, df); +} + +static inline int64_t msa_hsub_s_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return SIGNED_ODD(arg1, df) - SIGNED_EVEN(arg2, df); +} + +static inline int64_t msa_hsub_u_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    return UNSIGNED_ODD(arg1, df) - UNSIGNED_EVEN(arg2, df); +} + +static inline int64_t msa_mul_q_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int64_t q_min = DF_MIN_INT(df); +    int64_t q_max = DF_MAX_INT(df); + +    if (arg1 == q_min && arg2 == q_min) { +        return q_max; +    } +    return (arg1 * arg2) >> (DF_BITS(df) - 1); +} + +static inline int64_t msa_mulr_q_df(uint32_t df, int64_t arg1, int64_t arg2) +{ +    int64_t q_min = DF_MIN_INT(df); +    int64_t q_max = DF_MAX_INT(df); +    int64_t r_bit = 1 << (DF_BITS(df) - 2); + +    if (arg1 == q_min && arg2 == q_min) { +        return q_max; +    } +    return (arg1 * arg2 + r_bit) >> (DF_BITS(df) - 1); +} + +#define MSA_BINOP_DF(func) \ +void helper_msa_ ## func ## _df(CPUMIPSState *env, uint32_t df,         \ +                                uint32_t wd, uint32_t ws, uint32_t wt)  \ +{                                                                       \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                          \ +    wr_t *pws = &(env->active_fpu.fpr[ws].wr);                          \ +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr);                          \ +    uint32_t i;                                                         \ +                                                                        \ +    switch (df) {                                                       \ +    case DF_BYTE:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) {                    \ +            pwd->b[i] = msa_ ## func ## _df(df, pws->b[i], pwt->b[i]);  \ +        }                                                               \ +        break;                                                          \ +    case DF_HALF:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) {                    \ +            pwd->h[i] = msa_ ## func ## _df(df, pws->h[i], pwt->h[i]);  \ +        }                                                               \ +        break;                                                          \ +    case DF_WORD:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) {                    \ +            pwd->w[i] = msa_ ## func ## _df(df, pws->w[i], pwt->w[i]);  \ +        }                                                               \ +        break;                                                          \ +    case DF_DOUBLE:                                                     \ +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) {                  \ +            pwd->d[i] = msa_ ## func ## _df(df, pws->d[i], pwt->d[i]);  \ +        }                                                               \ +        break;                                                          \ +    default:                                                            \ +        assert(0);                                                      \ +    }                                                                   \ +} + +MSA_BINOP_DF(sll) +MSA_BINOP_DF(sra) +MSA_BINOP_DF(srl) +MSA_BINOP_DF(bclr) +MSA_BINOP_DF(bset) +MSA_BINOP_DF(bneg) +MSA_BINOP_DF(addv) +MSA_BINOP_DF(subv) +MSA_BINOP_DF(max_s) +MSA_BINOP_DF(max_u) +MSA_BINOP_DF(min_s) +MSA_BINOP_DF(min_u) +MSA_BINOP_DF(max_a) +MSA_BINOP_DF(min_a) +MSA_BINOP_DF(ceq) +MSA_BINOP_DF(clt_s) +MSA_BINOP_DF(clt_u) +MSA_BINOP_DF(cle_s) +MSA_BINOP_DF(cle_u) +MSA_BINOP_DF(add_a) +MSA_BINOP_DF(adds_a) +MSA_BINOP_DF(adds_s) +MSA_BINOP_DF(adds_u) +MSA_BINOP_DF(ave_s) +MSA_BINOP_DF(ave_u) +MSA_BINOP_DF(aver_s) +MSA_BINOP_DF(aver_u) +MSA_BINOP_DF(subs_s) +MSA_BINOP_DF(subs_u) +MSA_BINOP_DF(subsus_u) +MSA_BINOP_DF(subsuu_s) +MSA_BINOP_DF(asub_s) +MSA_BINOP_DF(asub_u) +MSA_BINOP_DF(mulv) +MSA_BINOP_DF(div_s) +MSA_BINOP_DF(div_u) +MSA_BINOP_DF(mod_s) +MSA_BINOP_DF(mod_u) +MSA_BINOP_DF(dotp_s) +MSA_BINOP_DF(dotp_u) +MSA_BINOP_DF(srar) +MSA_BINOP_DF(srlr) +MSA_BINOP_DF(hadd_s) +MSA_BINOP_DF(hadd_u) +MSA_BINOP_DF(hsub_s) +MSA_BINOP_DF(hsub_u) + +MSA_BINOP_DF(mul_q) +MSA_BINOP_DF(mulr_q) +#undef MSA_BINOP_DF + +void helper_msa_sld_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                       uint32_t ws, uint32_t rt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); + +    msa_sld_df(df, pwd, pws, env->active_tc.gpr[rt]); +} + +static inline int64_t msa_maddv_df(uint32_t df, int64_t dest, int64_t arg1, +                                   int64_t arg2) +{ +    return dest + arg1 * arg2; +} + +static inline int64_t msa_msubv_df(uint32_t df, int64_t dest, int64_t arg1, +                                   int64_t arg2) +{ +    return dest - arg1 * arg2; +} + +static inline int64_t msa_dpadd_s_df(uint32_t df, int64_t dest, int64_t arg1, +                                     int64_t arg2) +{ +    int64_t even_arg1; +    int64_t even_arg2; +    int64_t odd_arg1; +    int64_t odd_arg2; +    SIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); +    SIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); +    return dest + (even_arg1 * even_arg2) + (odd_arg1 * odd_arg2); +} + +static inline int64_t msa_dpadd_u_df(uint32_t df, int64_t dest, int64_t arg1, +                                     int64_t arg2) +{ +    int64_t even_arg1; +    int64_t even_arg2; +    int64_t odd_arg1; +    int64_t odd_arg2; +    UNSIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); +    UNSIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); +    return dest + (even_arg1 * even_arg2) + (odd_arg1 * odd_arg2); +} + +static inline int64_t msa_dpsub_s_df(uint32_t df, int64_t dest, int64_t arg1, +                                     int64_t arg2) +{ +    int64_t even_arg1; +    int64_t even_arg2; +    int64_t odd_arg1; +    int64_t odd_arg2; +    SIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); +    SIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); +    return dest - ((even_arg1 * even_arg2) + (odd_arg1 * odd_arg2)); +} + +static inline int64_t msa_dpsub_u_df(uint32_t df, int64_t dest, int64_t arg1, +                                     int64_t arg2) +{ +    int64_t even_arg1; +    int64_t even_arg2; +    int64_t odd_arg1; +    int64_t odd_arg2; +    UNSIGNED_EXTRACT(even_arg1, odd_arg1, arg1, df); +    UNSIGNED_EXTRACT(even_arg2, odd_arg2, arg2, df); +    return dest - ((even_arg1 * even_arg2) + (odd_arg1 * odd_arg2)); +} + +static inline int64_t msa_madd_q_df(uint32_t df, int64_t dest, int64_t arg1, +                                    int64_t arg2) +{ +    int64_t q_prod, q_ret; + +    int64_t q_max = DF_MAX_INT(df); +    int64_t q_min = DF_MIN_INT(df); + +    q_prod = arg1 * arg2; +    q_ret = ((dest << (DF_BITS(df) - 1)) + q_prod) >> (DF_BITS(df) - 1); + +    return (q_ret < q_min) ? q_min : (q_max < q_ret) ? q_max : q_ret; +} + +static inline int64_t msa_msub_q_df(uint32_t df, int64_t dest, int64_t arg1, +                                    int64_t arg2) +{ +    int64_t q_prod, q_ret; + +    int64_t q_max = DF_MAX_INT(df); +    int64_t q_min = DF_MIN_INT(df); + +    q_prod = arg1 * arg2; +    q_ret = ((dest << (DF_BITS(df) - 1)) - q_prod) >> (DF_BITS(df) - 1); + +    return (q_ret < q_min) ? q_min : (q_max < q_ret) ? q_max : q_ret; +} + +static inline int64_t msa_maddr_q_df(uint32_t df, int64_t dest, int64_t arg1, +                                     int64_t arg2) +{ +    int64_t q_prod, q_ret; + +    int64_t q_max = DF_MAX_INT(df); +    int64_t q_min = DF_MIN_INT(df); +    int64_t r_bit = 1 << (DF_BITS(df) - 2); + +    q_prod = arg1 * arg2; +    q_ret = ((dest << (DF_BITS(df) - 1)) + q_prod + r_bit) >> (DF_BITS(df) - 1); + +    return (q_ret < q_min) ? q_min : (q_max < q_ret) ? q_max : q_ret; +} + +static inline int64_t msa_msubr_q_df(uint32_t df, int64_t dest, int64_t arg1, +                                     int64_t arg2) +{ +    int64_t q_prod, q_ret; + +    int64_t q_max = DF_MAX_INT(df); +    int64_t q_min = DF_MIN_INT(df); +    int64_t r_bit = 1 << (DF_BITS(df) - 2); + +    q_prod = arg1 * arg2; +    q_ret = ((dest << (DF_BITS(df) - 1)) - q_prod + r_bit) >> (DF_BITS(df) - 1); + +    return (q_ret < q_min) ? q_min : (q_max < q_ret) ? q_max : q_ret; +} + +#define MSA_TEROP_DF(func) \ +void helper_msa_ ## func ## _df(CPUMIPSState *env, uint32_t df, uint32_t wd,   \ +                          uint32_t ws, uint32_t wt)                     \ +{                                                                       \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                          \ +    wr_t *pws = &(env->active_fpu.fpr[ws].wr);                          \ +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr);                          \ +    uint32_t i;                                                         \ +                                                                        \ +    switch (df) {                                                       \ +    case DF_BYTE:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) {                    \ +            pwd->b[i] = msa_ ## func ## _df(df, pwd->b[i], pws->b[i],   \ +                                            pwt->b[i]);                 \ +        }                                                               \ +        break;                                                          \ +    case DF_HALF:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) {                    \ +            pwd->h[i] = msa_ ## func ## _df(df, pwd->h[i], pws->h[i],   \ +                                            pwt->h[i]);                 \ +        }                                                               \ +        break;                                                          \ +    case DF_WORD:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) {                    \ +            pwd->w[i] = msa_ ## func ## _df(df, pwd->w[i], pws->w[i],   \ +                                            pwt->w[i]);                 \ +        }                                                               \ +        break;                                                          \ +    case DF_DOUBLE:                                                     \ +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) {                  \ +            pwd->d[i] = msa_ ## func ## _df(df, pwd->d[i], pws->d[i],   \ +                                            pwt->d[i]);                 \ +        }                                                               \ +        break;                                                          \ +    default:                                                            \ +        assert(0);                                                      \ +    }                                                                   \ +} + +MSA_TEROP_DF(maddv) +MSA_TEROP_DF(msubv) +MSA_TEROP_DF(dpadd_s) +MSA_TEROP_DF(dpadd_u) +MSA_TEROP_DF(dpsub_s) +MSA_TEROP_DF(dpsub_u) +MSA_TEROP_DF(binsl) +MSA_TEROP_DF(binsr) +MSA_TEROP_DF(madd_q) +MSA_TEROP_DF(msub_q) +MSA_TEROP_DF(maddr_q) +MSA_TEROP_DF(msubr_q) +#undef MSA_TEROP_DF + +static inline void msa_splat_df(uint32_t df, wr_t *pwd, +                                wr_t *pws, target_ulong rt) +{ +    uint32_t n = rt % DF_ELEMENTS(df); +    uint32_t i; + +    switch (df) { +    case DF_BYTE: +        for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { +            pwd->b[i] = pws->b[n]; +        } +        break; +    case DF_HALF: +        for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { +            pwd->h[i] = pws->h[n]; +        } +        break; +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            pwd->w[i] = pws->w[n]; +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            pwd->d[i] = pws->d[n]; +        } +       break; +    default: +        assert(0); +    } +} + +void helper_msa_splat_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t rt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); + +    msa_splat_df(df, pwd, pws, env->active_tc.gpr[rt]); +} + +#define MSA_DO_B MSA_DO(b) +#define MSA_DO_H MSA_DO(h) +#define MSA_DO_W MSA_DO(w) +#define MSA_DO_D MSA_DO(d) + +#define MSA_LOOP_B MSA_LOOP(B) +#define MSA_LOOP_H MSA_LOOP(H) +#define MSA_LOOP_W MSA_LOOP(W) +#define MSA_LOOP_D MSA_LOOP(D) + +#define MSA_LOOP_COND_B MSA_LOOP_COND(DF_BYTE) +#define MSA_LOOP_COND_H MSA_LOOP_COND(DF_HALF) +#define MSA_LOOP_COND_W MSA_LOOP_COND(DF_WORD) +#define MSA_LOOP_COND_D MSA_LOOP_COND(DF_DOUBLE) + +#define MSA_LOOP(DF) \ +        for (i = 0; i < (MSA_LOOP_COND_ ## DF) ; i++) { \ +            MSA_DO_ ## DF \ +        } + +#define MSA_FN_DF(FUNC)                                             \ +void helper_msa_##FUNC(CPUMIPSState *env, uint32_t df, uint32_t wd, \ +        uint32_t ws, uint32_t wt)                                   \ +{                                                                   \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                      \ +    wr_t *pws = &(env->active_fpu.fpr[ws].wr);                      \ +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr);                      \ +    wr_t wx, *pwx = &wx;                                            \ +    uint32_t i;                                                     \ +    switch (df) {                                                   \ +    case DF_BYTE:                                                   \ +        MSA_LOOP_B                                                  \ +        break;                                                      \ +    case DF_HALF:                                                   \ +        MSA_LOOP_H                                                  \ +        break;                                                      \ +    case DF_WORD:                                                   \ +        MSA_LOOP_W                                                  \ +        break;                                                      \ +    case DF_DOUBLE:                                                 \ +        MSA_LOOP_D                                                  \ +       break;                                                       \ +    default:                                                        \ +        assert(0);                                                  \ +    }                                                               \ +    msa_move_v(pwd, pwx);                                           \ +} + +#define MSA_LOOP_COND(DF) \ +            (DF_ELEMENTS(DF) / 2) + +#define Rb(pwr, i) (pwr->b[i]) +#define Lb(pwr, i) (pwr->b[i + DF_ELEMENTS(DF_BYTE)/2]) +#define Rh(pwr, i) (pwr->h[i]) +#define Lh(pwr, i) (pwr->h[i + DF_ELEMENTS(DF_HALF)/2]) +#define Rw(pwr, i) (pwr->w[i]) +#define Lw(pwr, i) (pwr->w[i + DF_ELEMENTS(DF_WORD)/2]) +#define Rd(pwr, i) (pwr->d[i]) +#define Ld(pwr, i) (pwr->d[i + DF_ELEMENTS(DF_DOUBLE)/2]) + +#define MSA_DO(DF)                      \ +    do {                                \ +        R##DF(pwx, i) = pwt->DF[2*i];   \ +        L##DF(pwx, i) = pws->DF[2*i];   \ +    } while (0); +MSA_FN_DF(pckev_df) +#undef MSA_DO + +#define MSA_DO(DF)                      \ +    do {                                \ +        R##DF(pwx, i) = pwt->DF[2*i+1]; \ +        L##DF(pwx, i) = pws->DF[2*i+1]; \ +    } while (0); +MSA_FN_DF(pckod_df) +#undef MSA_DO + +#define MSA_DO(DF)                      \ +    do {                                \ +        pwx->DF[2*i]   = L##DF(pwt, i); \ +        pwx->DF[2*i+1] = L##DF(pws, i); \ +    } while (0); +MSA_FN_DF(ilvl_df) +#undef MSA_DO + +#define MSA_DO(DF)                      \ +    do {                                \ +        pwx->DF[2*i]   = R##DF(pwt, i); \ +        pwx->DF[2*i+1] = R##DF(pws, i); \ +    } while (0); +MSA_FN_DF(ilvr_df) +#undef MSA_DO + +#define MSA_DO(DF)                      \ +    do {                                \ +        pwx->DF[2*i]   = pwt->DF[2*i];  \ +        pwx->DF[2*i+1] = pws->DF[2*i];  \ +    } while (0); +MSA_FN_DF(ilvev_df) +#undef MSA_DO + +#define MSA_DO(DF)                          \ +    do {                                    \ +        pwx->DF[2*i]   = pwt->DF[2*i+1];    \ +        pwx->DF[2*i+1] = pws->DF[2*i+1];    \ +    } while (0); +MSA_FN_DF(ilvod_df) +#undef MSA_DO +#undef MSA_LOOP_COND + +#define MSA_LOOP_COND(DF) \ +            (DF_ELEMENTS(DF)) + +#define MSA_DO(DF)                                                          \ +    do {                                                                    \ +        uint32_t n = DF_ELEMENTS(df);                                       \ +        uint32_t k = (pwd->DF[i] & 0x3f) % (2 * n);                         \ +        pwx->DF[i] =                                                        \ +            (pwd->DF[i] & 0xc0) ? 0 : k < n ? pwt->DF[k] : pws->DF[k - n];  \ +    } while (0); +MSA_FN_DF(vshf_df) +#undef MSA_DO +#undef MSA_LOOP_COND +#undef MSA_FN_DF + +void helper_msa_sldi_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t n) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); + +    msa_sld_df(df, pwd, pws, n); +} + +void helper_msa_splati_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                          uint32_t ws, uint32_t n) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); + +    msa_splat_df(df, pwd, pws, n); +} + +void helper_msa_copy_s_df(CPUMIPSState *env, uint32_t df, uint32_t rd, +                          uint32_t ws, uint32_t n) +{ +    n %= DF_ELEMENTS(df); + +    switch (df) { +    case DF_BYTE: +        env->active_tc.gpr[rd] = (int8_t)env->active_fpu.fpr[ws].wr.b[n]; +        break; +    case DF_HALF: +        env->active_tc.gpr[rd] = (int16_t)env->active_fpu.fpr[ws].wr.h[n]; +        break; +    case DF_WORD: +        env->active_tc.gpr[rd] = (int32_t)env->active_fpu.fpr[ws].wr.w[n]; +        break; +#ifdef TARGET_MIPS64 +    case DF_DOUBLE: +        env->active_tc.gpr[rd] = (int64_t)env->active_fpu.fpr[ws].wr.d[n]; +        break; +#endif +    default: +        assert(0); +    } +} + +void helper_msa_copy_u_df(CPUMIPSState *env, uint32_t df, uint32_t rd, +                          uint32_t ws, uint32_t n) +{ +    n %= DF_ELEMENTS(df); + +    switch (df) { +    case DF_BYTE: +        env->active_tc.gpr[rd] = (uint8_t)env->active_fpu.fpr[ws].wr.b[n]; +        break; +    case DF_HALF: +        env->active_tc.gpr[rd] = (uint16_t)env->active_fpu.fpr[ws].wr.h[n]; +        break; +    case DF_WORD: +        env->active_tc.gpr[rd] = (uint32_t)env->active_fpu.fpr[ws].wr.w[n]; +        break; +#ifdef TARGET_MIPS64 +    case DF_DOUBLE: +        env->active_tc.gpr[rd] = (uint64_t)env->active_fpu.fpr[ws].wr.d[n]; +        break; +#endif +    default: +        assert(0); +    } +} + +void helper_msa_insert_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                          uint32_t rs_num, uint32_t n) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    target_ulong rs = env->active_tc.gpr[rs_num]; + +    switch (df) { +    case DF_BYTE: +        pwd->b[n] = (int8_t)rs; +        break; +    case DF_HALF: +        pwd->h[n] = (int16_t)rs; +        break; +    case DF_WORD: +        pwd->w[n] = (int32_t)rs; +        break; +    case DF_DOUBLE: +        pwd->d[n] = (int64_t)rs; +        break; +    default: +        assert(0); +    } +} + +void helper_msa_insve_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t n) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); + +    switch (df) { +    case DF_BYTE: +        pwd->b[n] = (int8_t)pws->b[0]; +        break; +    case DF_HALF: +        pwd->h[n] = (int16_t)pws->h[0]; +        break; +    case DF_WORD: +        pwd->w[n] = (int32_t)pws->w[0]; +        break; +    case DF_DOUBLE: +        pwd->d[n] = (int64_t)pws->d[0]; +        break; +    default: +        assert(0); +    } +} + +void helper_msa_ctcmsa(CPUMIPSState *env, target_ulong elm, uint32_t cd) +{ +    switch (cd) { +    case 0: +        break; +    case 1: +        env->active_tc.msacsr = (int32_t)elm & MSACSR_MASK; +        restore_msa_fp_status(env); +        /* check exception */ +        if ((GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED) +            & GET_FP_CAUSE(env->active_tc.msacsr)) { +            helper_raise_exception(env, EXCP_MSAFPE); +        } +        break; +    } +} + +target_ulong helper_msa_cfcmsa(CPUMIPSState *env, uint32_t cs) +{ +    switch (cs) { +    case 0: +        return env->msair; +    case 1: +        return env->active_tc.msacsr & MSACSR_MASK; +    } +    return 0; +} + +void helper_msa_move_v(CPUMIPSState *env, uint32_t wd, uint32_t ws) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); + +    msa_move_v(pwd, pws); +} + +static inline int64_t msa_pcnt_df(uint32_t df, int64_t arg) +{ +    uint64_t x; + +    x = UNSIGNED(arg, df); + +    x = (x & 0x5555555555555555ULL) + ((x >>  1) & 0x5555555555555555ULL); +    x = (x & 0x3333333333333333ULL) + ((x >>  2) & 0x3333333333333333ULL); +    x = (x & 0x0F0F0F0F0F0F0F0FULL) + ((x >>  4) & 0x0F0F0F0F0F0F0F0FULL); +    x = (x & 0x00FF00FF00FF00FFULL) + ((x >>  8) & 0x00FF00FF00FF00FFULL); +    x = (x & 0x0000FFFF0000FFFFULL) + ((x >> 16) & 0x0000FFFF0000FFFFULL); +    x = (x & 0x00000000FFFFFFFFULL) + ((x >> 32)); + +    return x; +} + +static inline int64_t msa_nlzc_df(uint32_t df, int64_t arg) +{ +    uint64_t x, y; +    int n, c; + +    x = UNSIGNED(arg, df); +    n = DF_BITS(df); +    c = DF_BITS(df) / 2; + +    do { +        y = x >> c; +        if (y != 0) { +            n = n - c; +            x = y; +        } +        c = c >> 1; +    } while (c != 0); + +    return n - x; +} + +static inline int64_t msa_nloc_df(uint32_t df, int64_t arg) +{ +    return msa_nlzc_df(df, UNSIGNED((~arg), df)); +} + +void helper_msa_fill_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t rs) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    uint32_t i; + +    switch (df) { +    case DF_BYTE: +        for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) { +            pwd->b[i] = (int8_t)env->active_tc.gpr[rs]; +        } +        break; +    case DF_HALF: +        for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) { +            pwd->h[i] = (int16_t)env->active_tc.gpr[rs]; +        } +        break; +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            pwd->w[i] = (int32_t)env->active_tc.gpr[rs]; +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            pwd->d[i] = (int64_t)env->active_tc.gpr[rs]; +        } +       break; +    default: +        assert(0); +    } +} + +#define MSA_UNOP_DF(func) \ +void helper_msa_ ## func ## _df(CPUMIPSState *env, uint32_t df,         \ +                              uint32_t wd, uint32_t ws)                 \ +{                                                                       \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                          \ +    wr_t *pws = &(env->active_fpu.fpr[ws].wr);                          \ +    uint32_t i;                                                         \ +                                                                        \ +    switch (df) {                                                       \ +    case DF_BYTE:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_BYTE); i++) {                    \ +            pwd->b[i] = msa_ ## func ## _df(df, pws->b[i]);             \ +        }                                                               \ +        break;                                                          \ +    case DF_HALF:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_HALF); i++) {                    \ +            pwd->h[i] = msa_ ## func ## _df(df, pws->h[i]);             \ +        }                                                               \ +        break;                                                          \ +    case DF_WORD:                                                       \ +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) {                    \ +            pwd->w[i] = msa_ ## func ## _df(df, pws->w[i]);             \ +        }                                                               \ +        break;                                                          \ +    case DF_DOUBLE:                                                     \ +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) {                  \ +            pwd->d[i] = msa_ ## func ## _df(df, pws->d[i]);             \ +        }                                                               \ +        break;                                                          \ +    default:                                                            \ +        assert(0);                                                      \ +    }                                                                   \ +} + +MSA_UNOP_DF(nlzc) +MSA_UNOP_DF(nloc) +MSA_UNOP_DF(pcnt) +#undef MSA_UNOP_DF + +#define FLOAT_ONE32 make_float32(0x3f8 << 20) +#define FLOAT_ONE64 make_float64(0x3ffULL << 52) + +#define FLOAT_SNAN16 (float16_default_nan ^ 0x0220) +        /* 0x7c20 */ +#define FLOAT_SNAN32 (float32_default_nan ^ 0x00400020) +        /* 0x7f800020 */ +#define FLOAT_SNAN64 (float64_default_nan ^ 0x0008000000000020ULL) +        /* 0x7ff0000000000020 */ + +static inline void clear_msacsr_cause(CPUMIPSState *env) +{ +    SET_FP_CAUSE(env->active_tc.msacsr, 0); +} + +static inline void check_msacsr_cause(CPUMIPSState *env) +{ +    if ((GET_FP_CAUSE(env->active_tc.msacsr) & +            (GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED)) == 0) { +        UPDATE_FP_FLAGS(env->active_tc.msacsr, +                GET_FP_CAUSE(env->active_tc.msacsr)); +    } else { +        helper_raise_exception(env, EXCP_MSAFPE); +    } +} + +/* Flush-to-zero use cases for update_msacsr() */ +#define CLEAR_FS_UNDERFLOW 1 +#define CLEAR_IS_INEXACT   2 +#define RECIPROCAL_INEXACT 4 + +static inline int update_msacsr(CPUMIPSState *env, int action, int denormal) +{ +    int ieee_ex; + +    int c; +    int cause; +    int enable; + +    ieee_ex = get_float_exception_flags(&env->active_tc.msa_fp_status); + +    /* QEMU softfloat does not signal all underflow cases */ +    if (denormal) { +        ieee_ex |= float_flag_underflow; +    } + +    c = ieee_ex_to_mips(ieee_ex); +    enable = GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED; + +    /* Set Inexact (I) when flushing inputs to zero */ +    if ((ieee_ex & float_flag_input_denormal) && +            (env->active_tc.msacsr & MSACSR_FS_MASK) != 0) { +        if (action & CLEAR_IS_INEXACT) { +            c &= ~FP_INEXACT; +        } else { +            c |=  FP_INEXACT; +        } +    } + +    /* Set Inexact (I) and Underflow (U) when flushing outputs to zero */ +    if ((ieee_ex & float_flag_output_denormal) && +            (env->active_tc.msacsr & MSACSR_FS_MASK) != 0) { +        c |= FP_INEXACT; +        if (action & CLEAR_FS_UNDERFLOW) { +            c &= ~FP_UNDERFLOW; +        } else { +            c |=  FP_UNDERFLOW; +        } +    } + +    /* Set Inexact (I) when Overflow (O) is not enabled */ +    if ((c & FP_OVERFLOW) != 0 && (enable & FP_OVERFLOW) == 0) { +        c |= FP_INEXACT; +    } + +    /* Clear Exact Underflow when Underflow (U) is not enabled */ +    if ((c & FP_UNDERFLOW) != 0 && (enable & FP_UNDERFLOW) == 0 && +            (c & FP_INEXACT) == 0) { +        c &= ~FP_UNDERFLOW; +    } + +    /* Reciprocal operations set only Inexact when valid and not +       divide by zero */ +    if ((action & RECIPROCAL_INEXACT) && +            (c & (FP_INVALID | FP_DIV0)) == 0) { +        c = FP_INEXACT; +    } + +    cause = c & enable;    /* all current enabled exceptions */ + +    if (cause == 0) { +        /* No enabled exception, update the MSACSR Cause +         with all current exceptions */ +        SET_FP_CAUSE(env->active_tc.msacsr, +                (GET_FP_CAUSE(env->active_tc.msacsr) | c)); +    } else { +        /* Current exceptions are enabled */ +        if ((env->active_tc.msacsr & MSACSR_NX_MASK) == 0) { +            /* Exception(s) will trap, update MSACSR Cause +           with all enabled exceptions */ +            SET_FP_CAUSE(env->active_tc.msacsr, +                    (GET_FP_CAUSE(env->active_tc.msacsr) | c)); +        } +    } + +    return c; +} + +static inline int get_enabled_exceptions(const CPUMIPSState *env, int c) +{ +    int enable = GET_FP_ENABLE(env->active_tc.msacsr) | FP_UNIMPLEMENTED; +    return c & enable; +} + +static inline float16 float16_from_float32(int32 a, flag ieee, +                                           float_status *status) +{ +      float16 f_val; + +      f_val = float32_to_float16((float32)a, ieee, status); +      f_val = float16_maybe_silence_nan(f_val); + +      return a < 0 ? (f_val | (1 << 15)) : f_val; +} + +static inline float32 float32_from_float64(int64 a, float_status *status) +{ +      float32 f_val; + +      f_val = float64_to_float32((float64)a, status); +      f_val = float32_maybe_silence_nan(f_val); + +      return a < 0 ? (f_val | (1 << 31)) : f_val; +} + +static inline float32 float32_from_float16(int16_t a, flag ieee, +                                           float_status *status) +{ +      float32 f_val; + +      f_val = float16_to_float32((float16)a, ieee, status); +      f_val = float32_maybe_silence_nan(f_val); + +      return a < 0 ? (f_val | (1 << 31)) : f_val; +} + +static inline float64 float64_from_float32(int32 a, float_status *status) +{ +      float64 f_val; + +      f_val = float32_to_float64((float64)a, status); +      f_val = float64_maybe_silence_nan(f_val); + +      return a < 0 ? (f_val | (1ULL << 63)) : f_val; +} + +static inline float32 float32_from_q16(int16_t a, float_status *status) +{ +    float32 f_val; + +    /* conversion as integer and scaling */ +    f_val = int32_to_float32(a, status); +    f_val = float32_scalbn(f_val, -15, status); + +    return f_val; +} + +static inline float64 float64_from_q32(int32 a, float_status *status) +{ +    float64 f_val; + +    /* conversion as integer and scaling */ +    f_val = int32_to_float64(a, status); +    f_val = float64_scalbn(f_val, -31, status); + +    return f_val; +} + +static inline int16_t float32_to_q16(float32 a, float_status *status) +{ +    int32 q_val; +    int32 q_min = 0xffff8000; +    int32 q_max = 0x00007fff; + +    int ieee_ex; + +    if (float32_is_any_nan(a)) { +        float_raise(float_flag_invalid, status); +        return 0; +    } + +    /* scaling */ +    a = float32_scalbn(a, 15, status); + +    ieee_ex = get_float_exception_flags(status); +    set_float_exception_flags(ieee_ex & (~float_flag_underflow) +                             , status); + +    if (ieee_ex & float_flag_overflow) { +        float_raise(float_flag_inexact, status); +        return (int32)a < 0 ? q_min : q_max; +    } + +    /* conversion to int */ +    q_val = float32_to_int32(a, status); + +    ieee_ex = get_float_exception_flags(status); +    set_float_exception_flags(ieee_ex & (~float_flag_underflow) +                             , status); + +    if (ieee_ex & float_flag_invalid) { +        set_float_exception_flags(ieee_ex & (~float_flag_invalid) +                               , status); +        float_raise(float_flag_overflow | float_flag_inexact, status); +        return (int32)a < 0 ? q_min : q_max; +    } + +    if (q_val < q_min) { +        float_raise(float_flag_overflow | float_flag_inexact, status); +        return (int16_t)q_min; +    } + +    if (q_max < q_val) { +        float_raise(float_flag_overflow | float_flag_inexact, status); +        return (int16_t)q_max; +    } + +    return (int16_t)q_val; +} + +static inline int32 float64_to_q32(float64 a, float_status *status) +{ +    int64 q_val; +    int64 q_min = 0xffffffff80000000LL; +    int64 q_max = 0x000000007fffffffLL; + +    int ieee_ex; + +    if (float64_is_any_nan(a)) { +        float_raise(float_flag_invalid, status); +        return 0; +    } + +    /* scaling */ +    a = float64_scalbn(a, 31, status); + +    ieee_ex = get_float_exception_flags(status); +    set_float_exception_flags(ieee_ex & (~float_flag_underflow) +           , status); + +    if (ieee_ex & float_flag_overflow) { +        float_raise(float_flag_inexact, status); +        return (int64)a < 0 ? q_min : q_max; +    } + +    /* conversion to integer */ +    q_val = float64_to_int64(a, status); + +    ieee_ex = get_float_exception_flags(status); +    set_float_exception_flags(ieee_ex & (~float_flag_underflow) +           , status); + +    if (ieee_ex & float_flag_invalid) { +        set_float_exception_flags(ieee_ex & (~float_flag_invalid) +               , status); +        float_raise(float_flag_overflow | float_flag_inexact, status); +        return (int64)a < 0 ? q_min : q_max; +    } + +    if (q_val < q_min) { +        float_raise(float_flag_overflow | float_flag_inexact, status); +        return (int32)q_min; +    } + +    if (q_max < q_val) { +        float_raise(float_flag_overflow | float_flag_inexact, status); +        return (int32)q_max; +    } + +    return (int32)q_val; +} + +#define MSA_FLOAT_COND(DEST, OP, ARG1, ARG2, BITS, QUIET)                   \ +    do {                                                                    \ +        float_status *status = &env->active_tc.msa_fp_status;               \ +        int c;                                                              \ +        int64_t cond;                                                       \ +        set_float_exception_flags(0, status);                               \ +        if (!QUIET) {                                                       \ +            cond = float ## BITS ## _ ## OP(ARG1, ARG2, status);            \ +        } else {                                                            \ +            cond = float ## BITS ## _ ## OP ## _quiet(ARG1, ARG2, status);  \ +        }                                                                   \ +        DEST = cond ? M_MAX_UINT(BITS) : 0;                                 \ +        c = update_msacsr(env, CLEAR_IS_INEXACT, 0);                        \ +                                                                            \ +        if (get_enabled_exceptions(env, c)) {                               \ +            DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c;                    \ +        }                                                                   \ +    } while (0) + +#define MSA_FLOAT_AF(DEST, ARG1, ARG2, BITS, QUIET)                 \ +    do {                                                            \ +        MSA_FLOAT_COND(DEST, eq, ARG1, ARG2, BITS, QUIET);          \ +        if ((DEST & M_MAX_UINT(BITS)) == M_MAX_UINT(BITS)) {        \ +            DEST = 0;                                               \ +        }                                                           \ +    } while (0) + +#define MSA_FLOAT_UEQ(DEST, ARG1, ARG2, BITS, QUIET)                \ +    do {                                                            \ +        MSA_FLOAT_COND(DEST, unordered, ARG1, ARG2, BITS, QUIET);   \ +        if (DEST == 0) {                                            \ +            MSA_FLOAT_COND(DEST, eq, ARG1, ARG2, BITS, QUIET);      \ +        }                                                           \ +    } while (0) + +#define MSA_FLOAT_NE(DEST, ARG1, ARG2, BITS, QUIET)                 \ +    do {                                                            \ +        MSA_FLOAT_COND(DEST, lt, ARG1, ARG2, BITS, QUIET);          \ +        if (DEST == 0) {                                            \ +            MSA_FLOAT_COND(DEST, lt, ARG2, ARG1, BITS, QUIET);      \ +        }                                                           \ +    } while (0) + +#define MSA_FLOAT_UNE(DEST, ARG1, ARG2, BITS, QUIET)                \ +    do {                                                            \ +        MSA_FLOAT_COND(DEST, unordered, ARG1, ARG2, BITS, QUIET);   \ +        if (DEST == 0) {                                            \ +            MSA_FLOAT_COND(DEST, lt, ARG1, ARG2, BITS, QUIET);      \ +            if (DEST == 0) {                                        \ +                MSA_FLOAT_COND(DEST, lt, ARG2, ARG1, BITS, QUIET);  \ +            }                                                       \ +        }                                                           \ +    } while (0) + +#define MSA_FLOAT_ULE(DEST, ARG1, ARG2, BITS, QUIET)                \ +    do {                                                            \ +        MSA_FLOAT_COND(DEST, unordered, ARG1, ARG2, BITS, QUIET);   \ +        if (DEST == 0) {                                            \ +            MSA_FLOAT_COND(DEST, le, ARG1, ARG2, BITS, QUIET);      \ +        }                                                           \ +    } while (0) + +#define MSA_FLOAT_ULT(DEST, ARG1, ARG2, BITS, QUIET)                \ +    do {                                                            \ +        MSA_FLOAT_COND(DEST, unordered, ARG1, ARG2, BITS, QUIET);   \ +        if (DEST == 0) {                                            \ +            MSA_FLOAT_COND(DEST, lt, ARG1, ARG2, BITS, QUIET);      \ +        }                                                           \ +    } while (0) + +#define MSA_FLOAT_OR(DEST, ARG1, ARG2, BITS, QUIET)                 \ +    do {                                                            \ +        MSA_FLOAT_COND(DEST, le, ARG1, ARG2, BITS, QUIET);          \ +        if (DEST == 0) {                                            \ +            MSA_FLOAT_COND(DEST, le, ARG2, ARG1, BITS, QUIET);      \ +        }                                                           \ +    } while (0) + +static inline void compare_af(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                              wr_t *pwt, uint32_t df, int quiet) +{ +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_AF(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_AF(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +static inline void compare_un(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                              wr_t *pwt, uint32_t df, int quiet) +{ +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_COND(pwx->w[i], unordered, pws->w[i], pwt->w[i], 32, +                    quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_COND(pwx->d[i], unordered, pws->d[i], pwt->d[i], 64, +                    quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +static inline void compare_eq(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                              wr_t *pwt, uint32_t df, int quiet) +{ +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_COND(pwx->w[i], eq, pws->w[i], pwt->w[i], 32, quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_COND(pwx->d[i], eq, pws->d[i], pwt->d[i], 64, quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +static inline void compare_ueq(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                               wr_t *pwt, uint32_t df, int quiet) +{ +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UEQ(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UEQ(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +static inline void compare_lt(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                              wr_t *pwt, uint32_t df, int quiet) +{ +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_COND(pwx->w[i], lt, pws->w[i], pwt->w[i], 32, quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_COND(pwx->d[i], lt, pws->d[i], pwt->d[i], 64, quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +static inline void compare_ult(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                               wr_t *pwt, uint32_t df, int quiet) +{ +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_ULT(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_ULT(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +static inline void compare_le(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                              wr_t *pwt, uint32_t df, int quiet) +{ +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_COND(pwx->w[i], le, pws->w[i], pwt->w[i], 32, quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_COND(pwx->d[i], le, pws->d[i], pwt->d[i], 64, quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +static inline void compare_ule(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                               wr_t *pwt, uint32_t df, int quiet) +{ +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_ULE(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_ULE(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +static inline void compare_or(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                              wr_t *pwt, uint32_t df, int quiet) +{ +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_OR(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_OR(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +static inline void compare_une(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                               wr_t *pwt, uint32_t df, int quiet) +{ +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNE(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNE(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +static inline void compare_ne(CPUMIPSState *env, wr_t *pwd, wr_t *pws, +                              wr_t *pwt, uint32_t df, int quiet) { +    wr_t wx, *pwx = &wx; +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_NE(pwx->w[i], pws->w[i], pwt->w[i], 32, quiet); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_NE(pwx->d[i], pws->d[i], pwt->d[i], 64, quiet); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_fcaf_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_af(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcun_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_un(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fceq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_eq(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcueq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_ueq(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fclt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_lt(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcult_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_ult(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcle_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_le(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcule_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_ule(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fsaf_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_af(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsun_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_un(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fseq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_eq(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsueq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_ueq(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fslt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_lt(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsult_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_ult(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsle_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_le(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsule_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_ule(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fcor_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_or(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcune_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_une(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fcne_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_ne(env, pwd, pws, pwt, df, 1); +} + +void helper_msa_fsor_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_or(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsune_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_une(env, pwd, pws, pwt, df, 0); +} + +void helper_msa_fsne_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws, uint32_t wt) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    compare_ne(env, pwd, pws, pwt, df, 0); +} + +#define float16_is_zero(ARG) 0 +#define float16_is_zero_or_denormal(ARG) 0 + +#define IS_DENORMAL(ARG, BITS)                      \ +    (!float ## BITS ## _is_zero(ARG)                \ +    && float ## BITS ## _is_zero_or_denormal(ARG)) + +#define MSA_FLOAT_BINOP(DEST, OP, ARG1, ARG2, BITS)                         \ +    do {                                                                    \ +        float_status *status = &env->active_tc.msa_fp_status;               \ +        int c;                                                              \ +                                                                            \ +        set_float_exception_flags(0, status);                               \ +        DEST = float ## BITS ## _ ## OP(ARG1, ARG2, status);                \ +        c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS));                 \ +                                                                            \ +        if (get_enabled_exceptions(env, c)) {                               \ +            DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c;                    \ +        }                                                                   \ +    } while (0) + +void helper_msa_fadd_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_BINOP(pwx->w[i], add, pws->w[i], pwt->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_BINOP(pwx->d[i], add, pws->d[i], pwt->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); +    msa_move_v(pwd, pwx); +} + +void helper_msa_fsub_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_BINOP(pwx->w[i], sub, pws->w[i], pwt->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_BINOP(pwx->d[i], sub, pws->d[i], pwt->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); +    msa_move_v(pwd, pwx); +} + +void helper_msa_fmul_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_BINOP(pwx->w[i], mul, pws->w[i], pwt->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_BINOP(pwx->d[i], mul, pws->d[i], pwt->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_fdiv_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_BINOP(pwx->w[i], div, pws->w[i], pwt->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_BINOP(pwx->d[i], div, pws->d[i], pwt->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +#define MSA_FLOAT_MULADD(DEST, ARG1, ARG2, ARG3, NEGATE, BITS)              \ +    do {                                                                    \ +        float_status *status = &env->active_tc.msa_fp_status;               \ +        int c;                                                              \ +                                                                            \ +        set_float_exception_flags(0, status);                               \ +        DEST = float ## BITS ## _muladd(ARG2, ARG3, ARG1, NEGATE, status);  \ +        c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS));                 \ +                                                                            \ +        if (get_enabled_exceptions(env, c)) {                               \ +            DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c;                    \ +        }                                                                   \ +    } while (0) + +void helper_msa_fmadd_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_MULADD(pwx->w[i], pwd->w[i], +                           pws->w[i], pwt->w[i], 0, 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_MULADD(pwx->d[i], pwd->d[i], +                           pws->d[i], pwt->d[i], 0, 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_fmsub_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_MULADD(pwx->w[i], pwd->w[i], +                           pws->w[i], pwt->w[i], +                           float_muladd_negate_product, 32); +      } +      break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_MULADD(pwx->d[i], pwd->d[i], +                           pws->d[i], pwt->d[i], +                           float_muladd_negate_product, 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_fexp2_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_BINOP(pwx->w[i], scalbn, pws->w[i], +                            pwt->w[i] >  0x200 ?  0x200 : +                            pwt->w[i] < -0x200 ? -0x200 : pwt->w[i], +                            32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_BINOP(pwx->d[i], scalbn, pws->d[i], +                            pwt->d[i] >  0x1000 ?  0x1000 : +                            pwt->d[i] < -0x1000 ? -0x1000 : pwt->d[i], +                            64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +#define MSA_FLOAT_UNOP(DEST, OP, ARG, BITS)                                 \ +    do {                                                                    \ +        float_status *status = &env->active_tc.msa_fp_status;               \ +        int c;                                                              \ +                                                                            \ +        set_float_exception_flags(0, status);                               \ +        DEST = float ## BITS ## _ ## OP(ARG, status);                       \ +        c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS));                 \ +                                                                            \ +        if (get_enabled_exceptions(env, c)) {                               \ +            DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c;                    \ +        }                                                                   \ +    } while (0) + +void helper_msa_fexdo_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            /* Half precision floats come in two formats: standard +               IEEE and "ARM" format.  The latter gains extra exponent +               range by omitting the NaN/Inf encodings.  */ +            flag ieee = 1; + +            MSA_FLOAT_BINOP(Lh(pwx, i), from_float32, pws->w[i], ieee, 16); +            MSA_FLOAT_BINOP(Rh(pwx, i), from_float32, pwt->w[i], ieee, 16); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP(Lw(pwx, i), from_float64, pws->d[i], 32); +            MSA_FLOAT_UNOP(Rw(pwx, i), from_float64, pwt->d[i], 32); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); +    msa_move_v(pwd, pwx); +} + +#define MSA_FLOAT_UNOP_XD(DEST, OP, ARG, BITS, XBITS)                       \ +    do {                                                                    \ +        float_status *status = &env->active_tc.msa_fp_status;               \ +        int c;                                                              \ +                                                                            \ +        set_float_exception_flags(0, status);                               \ +        DEST = float ## BITS ## _ ## OP(ARG, status);                       \ +        c = update_msacsr(env, CLEAR_FS_UNDERFLOW, 0);                      \ +                                                                            \ +        if (get_enabled_exceptions(env, c)) {                               \ +            DEST = ((FLOAT_SNAN ## XBITS >> 6) << 6) | c;                   \ +        }                                                                   \ +    } while (0) + +void helper_msa_ftq_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                       uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP_XD(Lh(pwx, i), to_q16, pws->w[i], 32, 16); +            MSA_FLOAT_UNOP_XD(Rh(pwx, i), to_q16, pwt->w[i], 32, 16); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP_XD(Lw(pwx, i), to_q32, pws->d[i], 64, 32); +            MSA_FLOAT_UNOP_XD(Rw(pwx, i), to_q32, pwt->d[i], 64, 32); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +#define NUMBER_QNAN_PAIR(ARG1, ARG2, BITS)      \ +    !float ## BITS ## _is_any_nan(ARG1)         \ +    && float ## BITS ## _is_quiet_nan(ARG2) + +#define MSA_FLOAT_MAXOP(DEST, OP, ARG1, ARG2, BITS)                         \ +    do {                                                                    \ +        float_status *status = &env->active_tc.msa_fp_status;               \ +        int c;                                                              \ +                                                                            \ +        set_float_exception_flags(0, status);                               \ +        DEST = float ## BITS ## _ ## OP(ARG1, ARG2, status);                \ +        c = update_msacsr(env, 0, 0);                                       \ +                                                                            \ +        if (get_enabled_exceptions(env, c)) {                               \ +            DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c;                    \ +        }                                                                   \ +    } while (0) + +#define FMAXMIN_A(F, G, X, _S, _T, BITS)                            \ +    do {                                                            \ +        uint## BITS ##_t S = _S, T = _T;                            \ +        uint## BITS ##_t as, at, xs, xt, xd;                        \ +        if (NUMBER_QNAN_PAIR(S, T, BITS)) {                         \ +            T = S;                                                  \ +        }                                                           \ +        else if (NUMBER_QNAN_PAIR(T, S, BITS)) {                    \ +            S = T;                                                  \ +        }                                                           \ +        as = float## BITS ##_abs(S);                                \ +        at = float## BITS ##_abs(T);                                \ +        MSA_FLOAT_MAXOP(xs, F,  S,  T, BITS);                       \ +        MSA_FLOAT_MAXOP(xt, G,  S,  T, BITS);                       \ +        MSA_FLOAT_MAXOP(xd, F, as, at, BITS);                       \ +        X = (as == at || xd == float## BITS ##_abs(xs)) ? xs : xt;  \ +    } while (0) + +void helper_msa_fmin_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            if (NUMBER_QNAN_PAIR(pws->w[i], pwt->w[i], 32)) { +                MSA_FLOAT_MAXOP(pwx->w[i], min, pws->w[i], pws->w[i], 32); +            } else if (NUMBER_QNAN_PAIR(pwt->w[i], pws->w[i], 32)) { +                MSA_FLOAT_MAXOP(pwx->w[i], min, pwt->w[i], pwt->w[i], 32); +            } else { +                MSA_FLOAT_MAXOP(pwx->w[i], min, pws->w[i], pwt->w[i], 32); +            } +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            if (NUMBER_QNAN_PAIR(pws->d[i], pwt->d[i], 64)) { +                MSA_FLOAT_MAXOP(pwx->d[i], min, pws->d[i], pws->d[i], 64); +            } else if (NUMBER_QNAN_PAIR(pwt->d[i], pws->d[i], 64)) { +                MSA_FLOAT_MAXOP(pwx->d[i], min, pwt->d[i], pwt->d[i], 64); +            } else { +                MSA_FLOAT_MAXOP(pwx->d[i], min, pws->d[i], pwt->d[i], 64); +            } +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_fmin_a_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            FMAXMIN_A(min, max, pwx->w[i], pws->w[i], pwt->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            FMAXMIN_A(min, max, pwx->d[i], pws->d[i], pwt->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_fmax_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            if (NUMBER_QNAN_PAIR(pws->w[i], pwt->w[i], 32)) { +                MSA_FLOAT_MAXOP(pwx->w[i], max, pws->w[i], pws->w[i], 32); +            } else if (NUMBER_QNAN_PAIR(pwt->w[i], pws->w[i], 32)) { +                MSA_FLOAT_MAXOP(pwx->w[i], max, pwt->w[i], pwt->w[i], 32); +            } else { +                MSA_FLOAT_MAXOP(pwx->w[i], max, pws->w[i], pwt->w[i], 32); +            } +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            if (NUMBER_QNAN_PAIR(pws->d[i], pwt->d[i], 64)) { +                MSA_FLOAT_MAXOP(pwx->d[i], max, pws->d[i], pws->d[i], 64); +            } else if (NUMBER_QNAN_PAIR(pwt->d[i], pws->d[i], 64)) { +                MSA_FLOAT_MAXOP(pwx->d[i], max, pwt->d[i], pwt->d[i], 64); +            } else { +                MSA_FLOAT_MAXOP(pwx->d[i], max, pws->d[i], pwt->d[i], 64); +            } +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_fmax_a_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +        uint32_t ws, uint32_t wt) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    wr_t *pwt = &(env->active_fpu.fpr[wt].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            FMAXMIN_A(max, min, pwx->w[i], pws->w[i], pwt->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            FMAXMIN_A(max, min, pwx->d[i], pws->d[i], pwt->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_fclass_df(CPUMIPSState *env, uint32_t df, +        uint32_t wd, uint32_t ws) +{ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    if (df == DF_WORD) { +        pwd->w[0] = helper_float_class_s(pws->w[0]); +        pwd->w[1] = helper_float_class_s(pws->w[1]); +        pwd->w[2] = helper_float_class_s(pws->w[2]); +        pwd->w[3] = helper_float_class_s(pws->w[3]); +    } else { +        pwd->d[0] = helper_float_class_d(pws->d[0]); +        pwd->d[1] = helper_float_class_d(pws->d[1]); +    } +} + +#define MSA_FLOAT_UNOP0(DEST, OP, ARG, BITS)                                \ +    do {                                                                    \ +        float_status *status = &env->active_tc.msa_fp_status;               \ +        int c;                                                              \ +                                                                            \ +        set_float_exception_flags(0, status);                               \ +        DEST = float ## BITS ## _ ## OP(ARG, status);                       \ +        c = update_msacsr(env, CLEAR_FS_UNDERFLOW, 0);                      \ +                                                                            \ +        if (get_enabled_exceptions(env, c)) {                               \ +            DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c;                    \ +        } else if (float ## BITS ## _is_any_nan(ARG)) {                     \ +            DEST = 0;                                                       \ +        }                                                                   \ +    } while (0) + +void helper_msa_ftrunc_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                            uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP0(pwx->w[i], to_int32_round_to_zero, pws->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP0(pwx->d[i], to_int64_round_to_zero, pws->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_ftrunc_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                            uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP0(pwx->w[i], to_uint32_round_to_zero, pws->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP0(pwx->d[i], to_uint64_round_to_zero, pws->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_fsqrt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP(pwx->w[i], sqrt, pws->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP(pwx->d[i], sqrt, pws->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +#define MSA_FLOAT_RECIPROCAL(DEST, ARG, BITS)                               \ +    do {                                                                    \ +        float_status *status = &env->active_tc.msa_fp_status;               \ +        int c;                                                              \ +                                                                            \ +        set_float_exception_flags(0, status);                               \ +        DEST = float ## BITS ## _ ## div(FLOAT_ONE ## BITS, ARG, status);   \ +        c = update_msacsr(env, float ## BITS ## _is_infinity(ARG) ||        \ +                          float ## BITS ## _is_quiet_nan(DEST) ?            \ +                          0 : RECIPROCAL_INEXACT,                           \ +                          IS_DENORMAL(DEST, BITS));                         \ +                                                                            \ +        if (get_enabled_exceptions(env, c)) {                               \ +            DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c;                    \ +        }                                                                   \ +    } while (0) + +void helper_msa_frsqrt_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                          uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_RECIPROCAL(pwx->w[i], float32_sqrt(pws->w[i], +                    &env->active_tc.msa_fp_status), 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_RECIPROCAL(pwx->d[i], float64_sqrt(pws->d[i], +                    &env->active_tc.msa_fp_status), 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_frcp_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_RECIPROCAL(pwx->w[i], pws->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_RECIPROCAL(pwx->d[i], pws->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_frint_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP(pwx->w[i], round_to_int, pws->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP(pwx->d[i], round_to_int, pws->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +#define MSA_FLOAT_LOGB(DEST, ARG, BITS)                                     \ +    do {                                                                    \ +        float_status *status = &env->active_tc.msa_fp_status;               \ +        int c;                                                              \ +                                                                            \ +        set_float_exception_flags(0, status);                               \ +        set_float_rounding_mode(float_round_down, status);                  \ +        DEST = float ## BITS ## _ ## log2(ARG, status);                     \ +        DEST = float ## BITS ## _ ## round_to_int(DEST, status);            \ +        set_float_rounding_mode(ieee_rm[(env->active_tc.msacsr &            \ +                                         MSACSR_RM_MASK) >> MSACSR_RM],     \ +                                status);                                    \ +                                                                            \ +        set_float_exception_flags(get_float_exception_flags(status) &       \ +                                  (~float_flag_inexact),                    \ +                                  status);                                  \ +                                                                            \ +        c = update_msacsr(env, 0, IS_DENORMAL(DEST, BITS));                 \ +                                                                            \ +        if (get_enabled_exceptions(env, c)) {                               \ +            DEST = ((FLOAT_SNAN ## BITS >> 6) << 6) | c;                    \ +        }                                                                   \ +    } while (0) + +void helper_msa_flog2_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                         uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_LOGB(pwx->w[i], pws->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_LOGB(pwx->d[i], pws->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_fexupl_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                          uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            /* Half precision floats come in two formats: standard +               IEEE and "ARM" format.  The latter gains extra exponent +               range by omitting the NaN/Inf encodings.  */ +            flag ieee = 1; + +            MSA_FLOAT_BINOP(pwx->w[i], from_float16, Lh(pws, i), ieee, 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP(pwx->d[i], from_float32, Lw(pws, i), 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); +    msa_move_v(pwd, pwx); +} + +void helper_msa_fexupr_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                          uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            /* Half precision floats come in two formats: standard +               IEEE and "ARM" format.  The latter gains extra exponent +               range by omitting the NaN/Inf encodings.  */ +            flag ieee = 1; + +            MSA_FLOAT_BINOP(pwx->w[i], from_float16, Rh(pws, i), ieee, 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP(pwx->d[i], from_float32, Rw(pws, i), 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); +    msa_move_v(pwd, pwx); +} + +void helper_msa_ffql_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP(pwx->w[i], from_q16, Lh(pws, i), 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP(pwx->d[i], from_q32, Lw(pws, i), 64); +        } +        break; +    default: +        assert(0); +    } + +    msa_move_v(pwd, pwx); +} + +void helper_msa_ffqr_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                        uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP(pwx->w[i], from_q16, Rh(pws, i), 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP(pwx->d[i], from_q32, Rw(pws, i), 64); +        } +        break; +    default: +        assert(0); +    } + +    msa_move_v(pwd, pwx); +} + +void helper_msa_ftint_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                           uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP0(pwx->w[i], to_int32, pws->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP0(pwx->d[i], to_int64, pws->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_ftint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                           uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP0(pwx->w[i], to_uint32, pws->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP0(pwx->d[i], to_uint64, pws->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +#define float32_from_int32 int32_to_float32 +#define float32_from_uint32 uint32_to_float32 + +#define float64_from_int64 int64_to_float64 +#define float64_from_uint64 uint64_to_float64 + +void helper_msa_ffint_s_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                           uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP(pwx->w[i], from_int32, pws->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP(pwx->d[i], from_int64, pws->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} + +void helper_msa_ffint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, +                           uint32_t ws) +{ +    wr_t wx, *pwx = &wx; +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr); +    wr_t *pws = &(env->active_fpu.fpr[ws].wr); +    uint32_t i; + +    clear_msacsr_cause(env); + +    switch (df) { +    case DF_WORD: +        for (i = 0; i < DF_ELEMENTS(DF_WORD); i++) { +            MSA_FLOAT_UNOP(pwx->w[i], from_uint32, pws->w[i], 32); +        } +        break; +    case DF_DOUBLE: +        for (i = 0; i < DF_ELEMENTS(DF_DOUBLE); i++) { +            MSA_FLOAT_UNOP(pwx->d[i], from_uint64, pws->d[i], 64); +        } +        break; +    default: +        assert(0); +    } + +    check_msacsr_cause(env); + +    msa_move_v(pwd, pwx); +} diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c new file mode 100644 index 00000000..db4f6b94 --- /dev/null +++ b/target-mips/op_helper.c @@ -0,0 +1,3706 @@ +/* + *  MIPS emulation helpers for qemu. + * + *  Copyright (c) 2004-2005 Jocelyn Mayer + * + * 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 <stdlib.h> +#include "cpu.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" +#include "sysemu/kvm.h" + +#ifndef CONFIG_USER_ONLY +static inline void cpu_mips_tlb_flush (CPUMIPSState *env, int flush_global); +#endif + +/*****************************************************************************/ +/* Exceptions processing helpers */ + +static inline void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, +                                                        uint32_t exception, +                                                        int error_code, +                                                        uintptr_t pc) +{ +    CPUState *cs = CPU(mips_env_get_cpu(env)); + +    if (exception < EXCP_SC) { +        qemu_log("%s: %d %d\n", __func__, exception, error_code); +    } +    cs->exception_index = exception; +    env->error_code = error_code; + +    if (pc) { +        /* now we have a real cpu fault */ +        cpu_restore_state(cs, pc); +    } + +    cpu_loop_exit(cs); +} + +static inline void QEMU_NORETURN do_raise_exception(CPUMIPSState *env, +                                                    uint32_t exception, +                                                    uintptr_t pc) +{ +    do_raise_exception_err(env, exception, 0, pc); +} + +void helper_raise_exception_err(CPUMIPSState *env, uint32_t exception, +                                int error_code) +{ +    do_raise_exception_err(env, exception, error_code, 0); +} + +void helper_raise_exception(CPUMIPSState *env, uint32_t exception) +{ +    do_raise_exception(env, exception, 0); +} + +#if defined(CONFIG_USER_ONLY) +#define HELPER_LD(name, insn, type)                                     \ +static inline type do_##name(CPUMIPSState *env, target_ulong addr,      \ +                             int mem_idx)                               \ +{                                                                       \ +    return (type) cpu_##insn##_data(env, addr);                         \ +} +#else +#define HELPER_LD(name, insn, type)                                     \ +static inline type do_##name(CPUMIPSState *env, target_ulong addr,      \ +                             int mem_idx)                               \ +{                                                                       \ +    switch (mem_idx)                                                    \ +    {                                                                   \ +    case 0: return (type) cpu_##insn##_kernel(env, addr); break;        \ +    case 1: return (type) cpu_##insn##_super(env, addr); break;         \ +    default:                                                            \ +    case 2: return (type) cpu_##insn##_user(env, addr); break;          \ +    }                                                                   \ +} +#endif +HELPER_LD(lw, ldl, int32_t) +#if defined(TARGET_MIPS64) +HELPER_LD(ld, ldq, int64_t) +#endif +#undef HELPER_LD + +#if defined(CONFIG_USER_ONLY) +#define HELPER_ST(name, insn, type)                                     \ +static inline void do_##name(CPUMIPSState *env, target_ulong addr,      \ +                             type val, int mem_idx)                     \ +{                                                                       \ +    cpu_##insn##_data(env, addr, val);                                  \ +} +#else +#define HELPER_ST(name, insn, type)                                     \ +static inline void do_##name(CPUMIPSState *env, target_ulong addr,      \ +                             type val, int mem_idx)                     \ +{                                                                       \ +    switch (mem_idx)                                                    \ +    {                                                                   \ +    case 0: cpu_##insn##_kernel(env, addr, val); break;                 \ +    case 1: cpu_##insn##_super(env, addr, val); break;                  \ +    default:                                                            \ +    case 2: cpu_##insn##_user(env, addr, val); break;                   \ +    }                                                                   \ +} +#endif +HELPER_ST(sb, stb, uint8_t) +HELPER_ST(sw, stl, uint32_t) +#if defined(TARGET_MIPS64) +HELPER_ST(sd, stq, uint64_t) +#endif +#undef HELPER_ST + +target_ulong helper_clo (target_ulong arg1) +{ +    return clo32(arg1); +} + +target_ulong helper_clz (target_ulong arg1) +{ +    return clz32(arg1); +} + +#if defined(TARGET_MIPS64) +target_ulong helper_dclo (target_ulong arg1) +{ +    return clo64(arg1); +} + +target_ulong helper_dclz (target_ulong arg1) +{ +    return clz64(arg1); +} +#endif /* TARGET_MIPS64 */ + +/* 64 bits arithmetic for 32 bits hosts */ +static inline uint64_t get_HILO(CPUMIPSState *env) +{ +    return ((uint64_t)(env->active_tc.HI[0]) << 32) | (uint32_t)env->active_tc.LO[0]; +} + +static inline target_ulong set_HIT0_LO(CPUMIPSState *env, uint64_t HILO) +{ +    target_ulong tmp; +    env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); +    tmp = env->active_tc.HI[0] = (int32_t)(HILO >> 32); +    return tmp; +} + +static inline target_ulong set_HI_LOT0(CPUMIPSState *env, uint64_t HILO) +{ +    target_ulong tmp = env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); +    env->active_tc.HI[0] = (int32_t)(HILO >> 32); +    return tmp; +} + +/* Multiplication variants of the vr54xx. */ +target_ulong helper_muls(CPUMIPSState *env, target_ulong arg1, +                         target_ulong arg2) +{ +    return set_HI_LOT0(env, 0 - ((int64_t)(int32_t)arg1 * +                                 (int64_t)(int32_t)arg2)); +} + +target_ulong helper_mulsu(CPUMIPSState *env, target_ulong arg1, +                          target_ulong arg2) +{ +    return set_HI_LOT0(env, 0 - (uint64_t)(uint32_t)arg1 * +                       (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_macc(CPUMIPSState *env, target_ulong arg1, +                         target_ulong arg2) +{ +    return set_HI_LOT0(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * +                       (int64_t)(int32_t)arg2); +} + +target_ulong helper_macchi(CPUMIPSState *env, target_ulong arg1, +                           target_ulong arg2) +{ +    return set_HIT0_LO(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * +                       (int64_t)(int32_t)arg2); +} + +target_ulong helper_maccu(CPUMIPSState *env, target_ulong arg1, +                          target_ulong arg2) +{ +    return set_HI_LOT0(env, (uint64_t)get_HILO(env) + +                       (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_macchiu(CPUMIPSState *env, target_ulong arg1, +                            target_ulong arg2) +{ +    return set_HIT0_LO(env, (uint64_t)get_HILO(env) + +                       (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_msac(CPUMIPSState *env, target_ulong arg1, +                         target_ulong arg2) +{ +    return set_HI_LOT0(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * +                       (int64_t)(int32_t)arg2); +} + +target_ulong helper_msachi(CPUMIPSState *env, target_ulong arg1, +                           target_ulong arg2) +{ +    return set_HIT0_LO(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * +                       (int64_t)(int32_t)arg2); +} + +target_ulong helper_msacu(CPUMIPSState *env, target_ulong arg1, +                          target_ulong arg2) +{ +    return set_HI_LOT0(env, (uint64_t)get_HILO(env) - +                       (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_msachiu(CPUMIPSState *env, target_ulong arg1, +                            target_ulong arg2) +{ +    return set_HIT0_LO(env, (uint64_t)get_HILO(env) - +                       (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_mulhi(CPUMIPSState *env, target_ulong arg1, +                          target_ulong arg2) +{ +    return set_HIT0_LO(env, (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); +} + +target_ulong helper_mulhiu(CPUMIPSState *env, target_ulong arg1, +                           target_ulong arg2) +{ +    return set_HIT0_LO(env, (uint64_t)(uint32_t)arg1 * +                       (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_mulshi(CPUMIPSState *env, target_ulong arg1, +                           target_ulong arg2) +{ +    return set_HIT0_LO(env, 0 - (int64_t)(int32_t)arg1 * +                       (int64_t)(int32_t)arg2); +} + +target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1, +                            target_ulong arg2) +{ +    return set_HIT0_LO(env, 0 - (uint64_t)(uint32_t)arg1 * +                       (uint64_t)(uint32_t)arg2); +} + +static inline target_ulong bitswap(target_ulong v) +{ +    v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | +              ((v & (target_ulong)0x5555555555555555ULL) << 1); +    v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) | +              ((v & (target_ulong)0x3333333333333333ULL) << 2); +    v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) | +              ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4); +    return v; +} + +#ifdef TARGET_MIPS64 +target_ulong helper_dbitswap(target_ulong rt) +{ +    return bitswap(rt); +} +#endif + +target_ulong helper_bitswap(target_ulong rt) +{ +    return (int32_t)bitswap(rt); +} + +#ifndef CONFIG_USER_ONLY + +static inline hwaddr do_translate_address(CPUMIPSState *env, +                                                      target_ulong address, +                                                      int rw) +{ +    hwaddr lladdr; + +    lladdr = cpu_mips_translate_address(env, address, rw); + +    if (lladdr == -1LL) { +        cpu_loop_exit(CPU(mips_env_get_cpu(env))); +    } else { +        return lladdr; +    } +} + +#define HELPER_LD_ATOMIC(name, insn, almask)                                  \ +target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx)  \ +{                                                                             \ +    if (arg & almask) {                                                       \ +        env->CP0_BadVAddr = arg;                                              \ +        helper_raise_exception(env, EXCP_AdEL);                               \ +    }                                                                         \ +    env->lladdr = do_translate_address(env, arg, 0);                          \ +    env->llval = do_##insn(env, arg, mem_idx);                                \ +    return env->llval;                                                        \ +} +HELPER_LD_ATOMIC(ll, lw, 0x3) +#ifdef TARGET_MIPS64 +HELPER_LD_ATOMIC(lld, ld, 0x7) +#endif +#undef HELPER_LD_ATOMIC + +#define HELPER_ST_ATOMIC(name, ld_insn, st_insn, almask)                      \ +target_ulong helper_##name(CPUMIPSState *env, target_ulong arg1,              \ +                           target_ulong arg2, int mem_idx)                    \ +{                                                                             \ +    target_long tmp;                                                          \ +                                                                              \ +    if (arg2 & almask) {                                                      \ +        env->CP0_BadVAddr = arg2;                                             \ +        helper_raise_exception(env, EXCP_AdES);                               \ +    }                                                                         \ +    if (do_translate_address(env, arg2, 1) == env->lladdr) {                  \ +        tmp = do_##ld_insn(env, arg2, mem_idx);                               \ +        if (tmp == env->llval) {                                              \ +            do_##st_insn(env, arg2, arg1, mem_idx);                           \ +            return 1;                                                         \ +        }                                                                     \ +    }                                                                         \ +    return 0;                                                                 \ +} +HELPER_ST_ATOMIC(sc, lw, sw, 0x3) +#ifdef TARGET_MIPS64 +HELPER_ST_ATOMIC(scd, ld, sd, 0x7) +#endif +#undef HELPER_ST_ATOMIC +#endif + +#ifdef TARGET_WORDS_BIGENDIAN +#define GET_LMASK(v) ((v) & 3) +#define GET_OFFSET(addr, offset) (addr + (offset)) +#else +#define GET_LMASK(v) (((v) & 3) ^ 3) +#define GET_OFFSET(addr, offset) (addr - (offset)) +#endif + +void helper_swl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, +                int mem_idx) +{ +    do_sb(env, arg2, (uint8_t)(arg1 >> 24), mem_idx); + +    if (GET_LMASK(arg2) <= 2) +        do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 16), mem_idx); + +    if (GET_LMASK(arg2) <= 1) +        do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 8), mem_idx); + +    if (GET_LMASK(arg2) == 0) +        do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)arg1, mem_idx); +} + +void helper_swr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, +                int mem_idx) +{ +    do_sb(env, arg2, (uint8_t)arg1, mem_idx); + +    if (GET_LMASK(arg2) >= 1) +        do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx); + +    if (GET_LMASK(arg2) >= 2) +        do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx); + +    if (GET_LMASK(arg2) == 3) +        do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx); +} + +#if defined(TARGET_MIPS64) +/* "half" load and stores.  We must do the memory access inline, +   or fault handling won't work.  */ + +#ifdef TARGET_WORDS_BIGENDIAN +#define GET_LMASK64(v) ((v) & 7) +#else +#define GET_LMASK64(v) (((v) & 7) ^ 7) +#endif + +void helper_sdl(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, +                int mem_idx) +{ +    do_sb(env, arg2, (uint8_t)(arg1 >> 56), mem_idx); + +    if (GET_LMASK64(arg2) <= 6) +        do_sb(env, GET_OFFSET(arg2, 1), (uint8_t)(arg1 >> 48), mem_idx); + +    if (GET_LMASK64(arg2) <= 5) +        do_sb(env, GET_OFFSET(arg2, 2), (uint8_t)(arg1 >> 40), mem_idx); + +    if (GET_LMASK64(arg2) <= 4) +        do_sb(env, GET_OFFSET(arg2, 3), (uint8_t)(arg1 >> 32), mem_idx); + +    if (GET_LMASK64(arg2) <= 3) +        do_sb(env, GET_OFFSET(arg2, 4), (uint8_t)(arg1 >> 24), mem_idx); + +    if (GET_LMASK64(arg2) <= 2) +        do_sb(env, GET_OFFSET(arg2, 5), (uint8_t)(arg1 >> 16), mem_idx); + +    if (GET_LMASK64(arg2) <= 1) +        do_sb(env, GET_OFFSET(arg2, 6), (uint8_t)(arg1 >> 8), mem_idx); + +    if (GET_LMASK64(arg2) <= 0) +        do_sb(env, GET_OFFSET(arg2, 7), (uint8_t)arg1, mem_idx); +} + +void helper_sdr(CPUMIPSState *env, target_ulong arg1, target_ulong arg2, +                int mem_idx) +{ +    do_sb(env, arg2, (uint8_t)arg1, mem_idx); + +    if (GET_LMASK64(arg2) >= 1) +        do_sb(env, GET_OFFSET(arg2, -1), (uint8_t)(arg1 >> 8), mem_idx); + +    if (GET_LMASK64(arg2) >= 2) +        do_sb(env, GET_OFFSET(arg2, -2), (uint8_t)(arg1 >> 16), mem_idx); + +    if (GET_LMASK64(arg2) >= 3) +        do_sb(env, GET_OFFSET(arg2, -3), (uint8_t)(arg1 >> 24), mem_idx); + +    if (GET_LMASK64(arg2) >= 4) +        do_sb(env, GET_OFFSET(arg2, -4), (uint8_t)(arg1 >> 32), mem_idx); + +    if (GET_LMASK64(arg2) >= 5) +        do_sb(env, GET_OFFSET(arg2, -5), (uint8_t)(arg1 >> 40), mem_idx); + +    if (GET_LMASK64(arg2) >= 6) +        do_sb(env, GET_OFFSET(arg2, -6), (uint8_t)(arg1 >> 48), mem_idx); + +    if (GET_LMASK64(arg2) == 7) +        do_sb(env, GET_OFFSET(arg2, -7), (uint8_t)(arg1 >> 56), mem_idx); +} +#endif /* TARGET_MIPS64 */ + +static const int multiple_regs[] = { 16, 17, 18, 19, 20, 21, 22, 23, 30 }; + +void helper_lwm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, +                uint32_t mem_idx) +{ +    target_ulong base_reglist = reglist & 0xf; +    target_ulong do_r31 = reglist & 0x10; + +    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) { +        target_ulong i; + +        for (i = 0; i < base_reglist; i++) { +            env->active_tc.gpr[multiple_regs[i]] = +                (target_long)do_lw(env, addr, mem_idx); +            addr += 4; +        } +    } + +    if (do_r31) { +        env->active_tc.gpr[31] = (target_long)do_lw(env, addr, mem_idx); +    } +} + +void helper_swm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, +                uint32_t mem_idx) +{ +    target_ulong base_reglist = reglist & 0xf; +    target_ulong do_r31 = reglist & 0x10; + +    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) { +        target_ulong i; + +        for (i = 0; i < base_reglist; i++) { +            do_sw(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx); +            addr += 4; +        } +    } + +    if (do_r31) { +        do_sw(env, addr, env->active_tc.gpr[31], mem_idx); +    } +} + +#if defined(TARGET_MIPS64) +void helper_ldm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, +                uint32_t mem_idx) +{ +    target_ulong base_reglist = reglist & 0xf; +    target_ulong do_r31 = reglist & 0x10; + +    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) { +        target_ulong i; + +        for (i = 0; i < base_reglist; i++) { +            env->active_tc.gpr[multiple_regs[i]] = do_ld(env, addr, mem_idx); +            addr += 8; +        } +    } + +    if (do_r31) { +        env->active_tc.gpr[31] = do_ld(env, addr, mem_idx); +    } +} + +void helper_sdm(CPUMIPSState *env, target_ulong addr, target_ulong reglist, +                uint32_t mem_idx) +{ +    target_ulong base_reglist = reglist & 0xf; +    target_ulong do_r31 = reglist & 0x10; + +    if (base_reglist > 0 && base_reglist <= ARRAY_SIZE (multiple_regs)) { +        target_ulong i; + +        for (i = 0; i < base_reglist; i++) { +            do_sd(env, addr, env->active_tc.gpr[multiple_regs[i]], mem_idx); +            addr += 8; +        } +    } + +    if (do_r31) { +        do_sd(env, addr, env->active_tc.gpr[31], mem_idx); +    } +} +#endif + +#ifndef CONFIG_USER_ONLY +/* SMP helpers.  */ +static bool mips_vpe_is_wfi(MIPSCPU *c) +{ +    CPUState *cpu = CPU(c); +    CPUMIPSState *env = &c->env; + +    /* If the VPE is halted but otherwise active, it means it's waiting for +       an interrupt.  */ +    return cpu->halted && mips_vpe_active(env); +} + +static inline void mips_vpe_wake(MIPSCPU *c) +{ +    /* Dont set ->halted = 0 directly, let it be done via cpu_has_work +       because there might be other conditions that state that c should +       be sleeping.  */ +    cpu_interrupt(CPU(c), CPU_INTERRUPT_WAKE); +} + +static inline void mips_vpe_sleep(MIPSCPU *cpu) +{ +    CPUState *cs = CPU(cpu); + +    /* The VPE was shut off, really go to bed. +       Reset any old _WAKE requests.  */ +    cs->halted = 1; +    cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); +} + +static inline void mips_tc_wake(MIPSCPU *cpu, int tc) +{ +    CPUMIPSState *c = &cpu->env; + +    /* FIXME: TC reschedule.  */ +    if (mips_vpe_active(c) && !mips_vpe_is_wfi(cpu)) { +        mips_vpe_wake(cpu); +    } +} + +static inline void mips_tc_sleep(MIPSCPU *cpu, int tc) +{ +    CPUMIPSState *c = &cpu->env; + +    /* FIXME: TC reschedule.  */ +    if (!mips_vpe_active(c)) { +        mips_vpe_sleep(cpu); +    } +} + +/** + * mips_cpu_map_tc: + * @env: CPU from which mapping is performed. + * @tc: Should point to an int with the value of the global TC index. + * + * This function will transform @tc into a local index within the + * returned #CPUMIPSState. + */ +/* FIXME: This code assumes that all VPEs have the same number of TCs, +          which depends on runtime setup. Can probably be fixed by +          walking the list of CPUMIPSStates.  */ +static CPUMIPSState *mips_cpu_map_tc(CPUMIPSState *env, int *tc) +{ +    MIPSCPU *cpu; +    CPUState *cs; +    CPUState *other_cs; +    int vpe_idx; +    int tc_idx = *tc; + +    if (!(env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP))) { +        /* Not allowed to address other CPUs.  */ +        *tc = env->current_tc; +        return env; +    } + +    cs = CPU(mips_env_get_cpu(env)); +    vpe_idx = tc_idx / cs->nr_threads; +    *tc = tc_idx % cs->nr_threads; +    other_cs = qemu_get_cpu(vpe_idx); +    if (other_cs == NULL) { +        return env; +    } +    cpu = MIPS_CPU(other_cs); +    return &cpu->env; +} + +/* The per VPE CP0_Status register shares some fields with the per TC +   CP0_TCStatus registers. These fields are wired to the same registers, +   so changes to either of them should be reflected on both registers. + +   Also, EntryHi shares the bottom 8 bit ASID with TCStauts. + +   These helper call synchronizes the regs for a given cpu.  */ + +/* Called for updates to CP0_Status.  Defined in "cpu.h" for gdbstub.c.  */ +/* static inline void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, +                                     int tc);  */ + +/* Called for updates to CP0_TCStatus.  */ +static void sync_c0_tcstatus(CPUMIPSState *cpu, int tc, +                             target_ulong v) +{ +    uint32_t status; +    uint32_t tcu, tmx, tasid, tksu; +    uint32_t mask = ((1U << CP0St_CU3) +                       | (1 << CP0St_CU2) +                       | (1 << CP0St_CU1) +                       | (1 << CP0St_CU0) +                       | (1 << CP0St_MX) +                       | (3 << CP0St_KSU)); + +    tcu = (v >> CP0TCSt_TCU0) & 0xf; +    tmx = (v >> CP0TCSt_TMX) & 0x1; +    tasid = v & 0xff; +    tksu = (v >> CP0TCSt_TKSU) & 0x3; + +    status = tcu << CP0St_CU0; +    status |= tmx << CP0St_MX; +    status |= tksu << CP0St_KSU; + +    cpu->CP0_Status &= ~mask; +    cpu->CP0_Status |= status; + +    /* Sync the TASID with EntryHi.  */ +    cpu->CP0_EntryHi &= ~0xff; +    cpu->CP0_EntryHi |= tasid; + +    compute_hflags(cpu); +} + +/* Called for updates to CP0_EntryHi.  */ +static void sync_c0_entryhi(CPUMIPSState *cpu, int tc) +{ +    int32_t *tcst; +    uint32_t asid, v = cpu->CP0_EntryHi; + +    asid = v & 0xff; + +    if (tc == cpu->current_tc) { +        tcst = &cpu->active_tc.CP0_TCStatus; +    } else { +        tcst = &cpu->tcs[tc].CP0_TCStatus; +    } + +    *tcst &= ~0xff; +    *tcst |= asid; +} + +/* CP0 helpers */ +target_ulong helper_mfc0_mvpcontrol(CPUMIPSState *env) +{ +    return env->mvp->CP0_MVPControl; +} + +target_ulong helper_mfc0_mvpconf0(CPUMIPSState *env) +{ +    return env->mvp->CP0_MVPConf0; +} + +target_ulong helper_mfc0_mvpconf1(CPUMIPSState *env) +{ +    return env->mvp->CP0_MVPConf1; +} + +target_ulong helper_mfc0_random(CPUMIPSState *env) +{ +    return (int32_t)cpu_mips_get_random(env); +} + +target_ulong helper_mfc0_tcstatus(CPUMIPSState *env) +{ +    return env->active_tc.CP0_TCStatus; +} + +target_ulong helper_mftc0_tcstatus(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.CP0_TCStatus; +    else +        return other->tcs[other_tc].CP0_TCStatus; +} + +target_ulong helper_mfc0_tcbind(CPUMIPSState *env) +{ +    return env->active_tc.CP0_TCBind; +} + +target_ulong helper_mftc0_tcbind(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.CP0_TCBind; +    else +        return other->tcs[other_tc].CP0_TCBind; +} + +target_ulong helper_mfc0_tcrestart(CPUMIPSState *env) +{ +    return env->active_tc.PC; +} + +target_ulong helper_mftc0_tcrestart(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.PC; +    else +        return other->tcs[other_tc].PC; +} + +target_ulong helper_mfc0_tchalt(CPUMIPSState *env) +{ +    return env->active_tc.CP0_TCHalt; +} + +target_ulong helper_mftc0_tchalt(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.CP0_TCHalt; +    else +        return other->tcs[other_tc].CP0_TCHalt; +} + +target_ulong helper_mfc0_tccontext(CPUMIPSState *env) +{ +    return env->active_tc.CP0_TCContext; +} + +target_ulong helper_mftc0_tccontext(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.CP0_TCContext; +    else +        return other->tcs[other_tc].CP0_TCContext; +} + +target_ulong helper_mfc0_tcschedule(CPUMIPSState *env) +{ +    return env->active_tc.CP0_TCSchedule; +} + +target_ulong helper_mftc0_tcschedule(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.CP0_TCSchedule; +    else +        return other->tcs[other_tc].CP0_TCSchedule; +} + +target_ulong helper_mfc0_tcschefback(CPUMIPSState *env) +{ +    return env->active_tc.CP0_TCScheFBack; +} + +target_ulong helper_mftc0_tcschefback(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.CP0_TCScheFBack; +    else +        return other->tcs[other_tc].CP0_TCScheFBack; +} + +target_ulong helper_mfc0_count(CPUMIPSState *env) +{ +    return (int32_t)cpu_mips_get_count(env); +} + +target_ulong helper_mftc0_entryhi(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    return other->CP0_EntryHi; +} + +target_ulong helper_mftc0_cause(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    int32_t tccause; +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) { +        tccause = other->CP0_Cause; +    } else { +        tccause = other->CP0_Cause; +    } + +    return tccause; +} + +target_ulong helper_mftc0_status(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    return other->CP0_Status; +} + +target_ulong helper_mfc0_lladdr(CPUMIPSState *env) +{ +    return (int32_t)(env->lladdr >> env->CP0_LLAddr_shift); +} + +target_ulong helper_mfc0_watchlo(CPUMIPSState *env, uint32_t sel) +{ +    return (int32_t)env->CP0_WatchLo[sel]; +} + +target_ulong helper_mfc0_watchhi(CPUMIPSState *env, uint32_t sel) +{ +    return env->CP0_WatchHi[sel]; +} + +target_ulong helper_mfc0_debug(CPUMIPSState *env) +{ +    target_ulong t0 = env->CP0_Debug; +    if (env->hflags & MIPS_HFLAG_DM) +        t0 |= 1 << CP0DB_DM; + +    return t0; +} + +target_ulong helper_mftc0_debug(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    int32_t tcstatus; +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        tcstatus = other->active_tc.CP0_Debug_tcstatus; +    else +        tcstatus = other->tcs[other_tc].CP0_Debug_tcstatus; + +    /* XXX: Might be wrong, check with EJTAG spec. */ +    return (other->CP0_Debug & ~((1 << CP0DB_SSt) | (1 << CP0DB_Halt))) | +            (tcstatus & ((1 << CP0DB_SSt) | (1 << CP0DB_Halt))); +} + +#if defined(TARGET_MIPS64) +target_ulong helper_dmfc0_tcrestart(CPUMIPSState *env) +{ +    return env->active_tc.PC; +} + +target_ulong helper_dmfc0_tchalt(CPUMIPSState *env) +{ +    return env->active_tc.CP0_TCHalt; +} + +target_ulong helper_dmfc0_tccontext(CPUMIPSState *env) +{ +    return env->active_tc.CP0_TCContext; +} + +target_ulong helper_dmfc0_tcschedule(CPUMIPSState *env) +{ +    return env->active_tc.CP0_TCSchedule; +} + +target_ulong helper_dmfc0_tcschefback(CPUMIPSState *env) +{ +    return env->active_tc.CP0_TCScheFBack; +} + +target_ulong helper_dmfc0_lladdr(CPUMIPSState *env) +{ +    return env->lladdr >> env->CP0_LLAddr_shift; +} + +target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel) +{ +    return env->CP0_WatchLo[sel]; +} +#endif /* TARGET_MIPS64 */ + +void helper_mtc0_index(CPUMIPSState *env, target_ulong arg1) +{ +    uint32_t index_p = env->CP0_Index & 0x80000000; +    uint32_t tlb_index = arg1 & 0x7fffffff; +    if (tlb_index < env->tlb->nb_tlb) { +        if (env->insn_flags & ISA_MIPS32R6) { +            index_p |= arg1 & 0x80000000; +        } +        env->CP0_Index = index_p | tlb_index; +    } +} + +void helper_mtc0_mvpcontrol(CPUMIPSState *env, target_ulong arg1) +{ +    uint32_t mask = 0; +    uint32_t newval; + +    if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) +        mask |= (1 << CP0MVPCo_CPA) | (1 << CP0MVPCo_VPC) | +                (1 << CP0MVPCo_EVP); +    if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) +        mask |= (1 << CP0MVPCo_STLB); +    newval = (env->mvp->CP0_MVPControl & ~mask) | (arg1 & mask); + +    // TODO: Enable/disable shared TLB, enable/disable VPEs. + +    env->mvp->CP0_MVPControl = newval; +} + +void helper_mtc0_vpecontrol(CPUMIPSState *env, target_ulong arg1) +{ +    uint32_t mask; +    uint32_t newval; + +    mask = (1 << CP0VPECo_YSI) | (1 << CP0VPECo_GSI) | +           (1 << CP0VPECo_TE) | (0xff << CP0VPECo_TargTC); +    newval = (env->CP0_VPEControl & ~mask) | (arg1 & mask); + +    /* Yield scheduler intercept not implemented. */ +    /* Gating storage scheduler intercept not implemented. */ + +    // TODO: Enable/disable TCs. + +    env->CP0_VPEControl = newval; +} + +void helper_mttc0_vpecontrol(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); +    uint32_t mask; +    uint32_t newval; + +    mask = (1 << CP0VPECo_YSI) | (1 << CP0VPECo_GSI) | +           (1 << CP0VPECo_TE) | (0xff << CP0VPECo_TargTC); +    newval = (other->CP0_VPEControl & ~mask) | (arg1 & mask); + +    /* TODO: Enable/disable TCs.  */ + +    other->CP0_VPEControl = newval; +} + +target_ulong helper_mftc0_vpecontrol(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); +    /* FIXME: Mask away return zero on read bits.  */ +    return other->CP0_VPEControl; +} + +target_ulong helper_mftc0_vpeconf0(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    return other->CP0_VPEConf0; +} + +void helper_mtc0_vpeconf0(CPUMIPSState *env, target_ulong arg1) +{ +    uint32_t mask = 0; +    uint32_t newval; + +    if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) { +        if (env->CP0_VPEConf0 & (1 << CP0VPEC0_VPA)) +            mask |= (0xff << CP0VPEC0_XTC); +        mask |= (1 << CP0VPEC0_MVP) | (1 << CP0VPEC0_VPA); +    } +    newval = (env->CP0_VPEConf0 & ~mask) | (arg1 & mask); + +    // TODO: TC exclusive handling due to ERL/EXL. + +    env->CP0_VPEConf0 = newval; +} + +void helper_mttc0_vpeconf0(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); +    uint32_t mask = 0; +    uint32_t newval; + +    mask |= (1 << CP0VPEC0_MVP) | (1 << CP0VPEC0_VPA); +    newval = (other->CP0_VPEConf0 & ~mask) | (arg1 & mask); + +    /* TODO: TC exclusive handling due to ERL/EXL.  */ +    other->CP0_VPEConf0 = newval; +} + +void helper_mtc0_vpeconf1(CPUMIPSState *env, target_ulong arg1) +{ +    uint32_t mask = 0; +    uint32_t newval; + +    if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) +        mask |= (0xff << CP0VPEC1_NCX) | (0xff << CP0VPEC1_NCP2) | +                (0xff << CP0VPEC1_NCP1); +    newval = (env->CP0_VPEConf1 & ~mask) | (arg1 & mask); + +    /* UDI not implemented. */ +    /* CP2 not implemented. */ + +    // TODO: Handle FPU (CP1) binding. + +    env->CP0_VPEConf1 = newval; +} + +void helper_mtc0_yqmask(CPUMIPSState *env, target_ulong arg1) +{ +    /* Yield qualifier inputs not implemented. */ +    env->CP0_YQMask = 0x00000000; +} + +void helper_mtc0_vpeopt(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_VPEOpt = arg1 & 0x0000ffff; +} + +#define MTC0_ENTRYLO_MASK(env) ((env->PAMask >> 6) & 0x3FFFFFFF) + +void helper_mtc0_entrylo0(CPUMIPSState *env, target_ulong arg1) +{ +    /* 1k pages not implemented */ +    target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE)); +    env->CP0_EntryLo0 = (arg1 & MTC0_ENTRYLO_MASK(env)) +                        | (rxi << (CP0EnLo_XI - 30)); +} + +#if defined(TARGET_MIPS64) +#define DMTC0_ENTRYLO_MASK(env) (env->PAMask >> 6) + +void helper_dmtc0_entrylo0(CPUMIPSState *env, uint64_t arg1) +{ +    uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32); +    env->CP0_EntryLo0 = (arg1 & DMTC0_ENTRYLO_MASK(env)) | rxi; +} +#endif + +void helper_mtc0_tcstatus(CPUMIPSState *env, target_ulong arg1) +{ +    uint32_t mask = env->CP0_TCStatus_rw_bitmask; +    uint32_t newval; + +    newval = (env->active_tc.CP0_TCStatus & ~mask) | (arg1 & mask); + +    env->active_tc.CP0_TCStatus = newval; +    sync_c0_tcstatus(env, env->current_tc, newval); +} + +void helper_mttc0_tcstatus(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        other->active_tc.CP0_TCStatus = arg1; +    else +        other->tcs[other_tc].CP0_TCStatus = arg1; +    sync_c0_tcstatus(other, other_tc, arg1); +} + +void helper_mtc0_tcbind(CPUMIPSState *env, target_ulong arg1) +{ +    uint32_t mask = (1 << CP0TCBd_TBE); +    uint32_t newval; + +    if (env->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) +        mask |= (1 << CP0TCBd_CurVPE); +    newval = (env->active_tc.CP0_TCBind & ~mask) | (arg1 & mask); +    env->active_tc.CP0_TCBind = newval; +} + +void helper_mttc0_tcbind(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    uint32_t mask = (1 << CP0TCBd_TBE); +    uint32_t newval; +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other->mvp->CP0_MVPControl & (1 << CP0MVPCo_VPC)) +        mask |= (1 << CP0TCBd_CurVPE); +    if (other_tc == other->current_tc) { +        newval = (other->active_tc.CP0_TCBind & ~mask) | (arg1 & mask); +        other->active_tc.CP0_TCBind = newval; +    } else { +        newval = (other->tcs[other_tc].CP0_TCBind & ~mask) | (arg1 & mask); +        other->tcs[other_tc].CP0_TCBind = newval; +    } +} + +void helper_mtc0_tcrestart(CPUMIPSState *env, target_ulong arg1) +{ +    env->active_tc.PC = arg1; +    env->active_tc.CP0_TCStatus &= ~(1 << CP0TCSt_TDS); +    env->lladdr = 0ULL; +    /* MIPS16 not implemented. */ +} + +void helper_mttc0_tcrestart(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) { +        other->active_tc.PC = arg1; +        other->active_tc.CP0_TCStatus &= ~(1 << CP0TCSt_TDS); +        other->lladdr = 0ULL; +        /* MIPS16 not implemented. */ +    } else { +        other->tcs[other_tc].PC = arg1; +        other->tcs[other_tc].CP0_TCStatus &= ~(1 << CP0TCSt_TDS); +        other->lladdr = 0ULL; +        /* MIPS16 not implemented. */ +    } +} + +void helper_mtc0_tchalt(CPUMIPSState *env, target_ulong arg1) +{ +    MIPSCPU *cpu = mips_env_get_cpu(env); + +    env->active_tc.CP0_TCHalt = arg1 & 0x1; + +    // TODO: Halt TC / Restart (if allocated+active) TC. +    if (env->active_tc.CP0_TCHalt & 1) { +        mips_tc_sleep(cpu, env->current_tc); +    } else { +        mips_tc_wake(cpu, env->current_tc); +    } +} + +void helper_mttc0_tchalt(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); +    MIPSCPU *other_cpu = mips_env_get_cpu(other); + +    // TODO: Halt TC / Restart (if allocated+active) TC. + +    if (other_tc == other->current_tc) +        other->active_tc.CP0_TCHalt = arg1; +    else +        other->tcs[other_tc].CP0_TCHalt = arg1; + +    if (arg1 & 1) { +        mips_tc_sleep(other_cpu, other_tc); +    } else { +        mips_tc_wake(other_cpu, other_tc); +    } +} + +void helper_mtc0_tccontext(CPUMIPSState *env, target_ulong arg1) +{ +    env->active_tc.CP0_TCContext = arg1; +} + +void helper_mttc0_tccontext(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        other->active_tc.CP0_TCContext = arg1; +    else +        other->tcs[other_tc].CP0_TCContext = arg1; +} + +void helper_mtc0_tcschedule(CPUMIPSState *env, target_ulong arg1) +{ +    env->active_tc.CP0_TCSchedule = arg1; +} + +void helper_mttc0_tcschedule(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        other->active_tc.CP0_TCSchedule = arg1; +    else +        other->tcs[other_tc].CP0_TCSchedule = arg1; +} + +void helper_mtc0_tcschefback(CPUMIPSState *env, target_ulong arg1) +{ +    env->active_tc.CP0_TCScheFBack = arg1; +} + +void helper_mttc0_tcschefback(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        other->active_tc.CP0_TCScheFBack = arg1; +    else +        other->tcs[other_tc].CP0_TCScheFBack = arg1; +} + +void helper_mtc0_entrylo1(CPUMIPSState *env, target_ulong arg1) +{ +    /* 1k pages not implemented */ +    target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE)); +    env->CP0_EntryLo1 = (arg1 & MTC0_ENTRYLO_MASK(env)) +                        | (rxi << (CP0EnLo_XI - 30)); +} + +#if defined(TARGET_MIPS64) +void helper_dmtc0_entrylo1(CPUMIPSState *env, uint64_t arg1) +{ +    uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32); +    env->CP0_EntryLo1 = (arg1 & DMTC0_ENTRYLO_MASK(env)) | rxi; +} +#endif + +void helper_mtc0_context(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_Context = (env->CP0_Context & 0x007FFFFF) | (arg1 & ~0x007FFFFF); +} + +void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) +{ +    uint64_t mask = arg1 >> (TARGET_PAGE_BITS + 1); +    if (!(env->insn_flags & ISA_MIPS32R6) || (arg1 == ~0) || +        (mask == 0x0000 || mask == 0x0003 || mask == 0x000F || +         mask == 0x003F || mask == 0x00FF || mask == 0x03FF || +         mask == 0x0FFF || mask == 0x3FFF || mask == 0xFFFF)) { +        env->CP0_PageMask = arg1 & (0x1FFFFFFF & (TARGET_PAGE_MASK << 1)); +    } +} + +void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) +{ +    /* SmartMIPS not implemented */ +    /* 1k pages not implemented */ +    env->CP0_PageGrain = (arg1 & env->CP0_PageGrain_rw_bitmask) | +                         (env->CP0_PageGrain & ~env->CP0_PageGrain_rw_bitmask); +    compute_hflags(env); +    restore_pamask(env); +} + +void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1) +{ +    if (env->insn_flags & ISA_MIPS32R6) { +        if (arg1 < env->tlb->nb_tlb) { +            env->CP0_Wired = arg1; +        } +    } else { +        env->CP0_Wired = arg1 % env->tlb->nb_tlb; +    } +} + +void helper_mtc0_srsconf0(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_SRSConf0 |= arg1 & env->CP0_SRSConf0_rw_bitmask; +} + +void helper_mtc0_srsconf1(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_SRSConf1 |= arg1 & env->CP0_SRSConf1_rw_bitmask; +} + +void helper_mtc0_srsconf2(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_SRSConf2 |= arg1 & env->CP0_SRSConf2_rw_bitmask; +} + +void helper_mtc0_srsconf3(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_SRSConf3 |= arg1 & env->CP0_SRSConf3_rw_bitmask; +} + +void helper_mtc0_srsconf4(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_SRSConf4 |= arg1 & env->CP0_SRSConf4_rw_bitmask; +} + +void helper_mtc0_hwrena(CPUMIPSState *env, target_ulong arg1) +{ +    uint32_t mask = 0x0000000F; + +    if (env->CP0_Config3 & (1 << CP0C3_ULRI)) { +        mask |= (1 << 29); + +        if (arg1 & (1 << 29)) { +            env->hflags |= MIPS_HFLAG_HWRENA_ULR; +        } else { +            env->hflags &= ~MIPS_HFLAG_HWRENA_ULR; +        } +    } + +    env->CP0_HWREna = arg1 & mask; +} + +void helper_mtc0_count(CPUMIPSState *env, target_ulong arg1) +{ +    cpu_mips_store_count(env, arg1); +} + +void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1) +{ +    target_ulong old, val, mask; +    mask = (TARGET_PAGE_MASK << 1) | 0xFF; +    if (((env->CP0_Config4 >> CP0C4_IE) & 0x3) >= 2) { +        mask |= 1 << CP0EnHi_EHINV; +    } + +    /* 1k pages not implemented */ +#if defined(TARGET_MIPS64) +    if (env->insn_flags & ISA_MIPS32R6) { +        int entryhi_r = extract64(arg1, 62, 2); +        int config0_at = extract32(env->CP0_Config0, 13, 2); +        bool no_supervisor = (env->CP0_Status_rw_bitmask & 0x8) == 0; +        if ((entryhi_r == 2) || +            (entryhi_r == 1 && (no_supervisor || config0_at == 1))) { +            /* skip EntryHi.R field if new value is reserved */ +            mask &= ~(0x3ull << 62); +        } +    } +    mask &= env->SEGMask; +#endif +    old = env->CP0_EntryHi; +    val = (arg1 & mask) | (old & ~mask); +    env->CP0_EntryHi = val; +    if (env->CP0_Config3 & (1 << CP0C3_MT)) { +        sync_c0_entryhi(env, env->current_tc); +    } +    /* If the ASID changes, flush qemu's TLB.  */ +    if ((old & 0xFF) != (val & 0xFF)) +        cpu_mips_tlb_flush(env, 1); +} + +void helper_mttc0_entryhi(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    other->CP0_EntryHi = arg1; +    sync_c0_entryhi(other, other_tc); +} + +void helper_mtc0_compare(CPUMIPSState *env, target_ulong arg1) +{ +    cpu_mips_store_compare(env, arg1); +} + +void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1) +{ +    MIPSCPU *cpu = mips_env_get_cpu(env); +    uint32_t val, old; + +    old = env->CP0_Status; +    cpu_mips_store_status(env, arg1); +    val = env->CP0_Status; + +    if (qemu_loglevel_mask(CPU_LOG_EXEC)) { +        qemu_log("Status %08x (%08x) => %08x (%08x) Cause %08x", +                old, old & env->CP0_Cause & CP0Ca_IP_mask, +                val, val & env->CP0_Cause & CP0Ca_IP_mask, +                env->CP0_Cause); +        switch (env->hflags & MIPS_HFLAG_KSU) { +        case MIPS_HFLAG_UM: qemu_log(", UM\n"); break; +        case MIPS_HFLAG_SM: qemu_log(", SM\n"); break; +        case MIPS_HFLAG_KM: qemu_log("\n"); break; +        default: +            cpu_abort(CPU(cpu), "Invalid MMU mode!\n"); +            break; +        } +    } +} + +void helper_mttc0_status(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    uint32_t mask = env->CP0_Status_rw_bitmask & ~0xf1000018; +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    other->CP0_Status = (other->CP0_Status & ~mask) | (arg1 & mask); +    sync_c0_status(env, other, other_tc); +} + +void helper_mtc0_intctl(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_IntCtl = (env->CP0_IntCtl & ~0x000003e0) | (arg1 & 0x000003e0); +} + +void helper_mtc0_srsctl(CPUMIPSState *env, target_ulong arg1) +{ +    uint32_t mask = (0xf << CP0SRSCtl_ESS) | (0xf << CP0SRSCtl_PSS); +    env->CP0_SRSCtl = (env->CP0_SRSCtl & ~mask) | (arg1 & mask); +} + +void helper_mtc0_cause(CPUMIPSState *env, target_ulong arg1) +{ +    cpu_mips_store_cause(env, arg1); +} + +void helper_mttc0_cause(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    cpu_mips_store_cause(other, arg1); +} + +target_ulong helper_mftc0_epc(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    return other->CP0_EPC; +} + +target_ulong helper_mftc0_ebase(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    return other->CP0_EBase; +} + +void helper_mtc0_ebase(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_EBase = (env->CP0_EBase & ~0x3FFFF000) | (arg1 & 0x3FFFF000); +} + +void helper_mttc0_ebase(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); +    other->CP0_EBase = (other->CP0_EBase & ~0x3FFFF000) | (arg1 & 0x3FFFF000); +} + +target_ulong helper_mftc0_configx(CPUMIPSState *env, target_ulong idx) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    switch (idx) { +    case 0: return other->CP0_Config0; +    case 1: return other->CP0_Config1; +    case 2: return other->CP0_Config2; +    case 3: return other->CP0_Config3; +    /* 4 and 5 are reserved.  */ +    case 6: return other->CP0_Config6; +    case 7: return other->CP0_Config7; +    default: +        break; +    } +    return 0; +} + +void helper_mtc0_config0(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_Config0 = (env->CP0_Config0 & 0x81FFFFF8) | (arg1 & 0x00000007); +} + +void helper_mtc0_config2(CPUMIPSState *env, target_ulong arg1) +{ +    /* tertiary/secondary caches not implemented */ +    env->CP0_Config2 = (env->CP0_Config2 & 0x8FFF0FFF); +} + +void helper_mtc0_config3(CPUMIPSState *env, target_ulong arg1) +{ +    if (env->insn_flags & ASE_MICROMIPS) { +        env->CP0_Config3 = (env->CP0_Config3 & ~(1 << CP0C3_ISA_ON_EXC)) | +                           (arg1 & (1 << CP0C3_ISA_ON_EXC)); +    } +} + +void helper_mtc0_config4(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_Config4 = (env->CP0_Config4 & (~env->CP0_Config4_rw_bitmask)) | +                       (arg1 & env->CP0_Config4_rw_bitmask); +} + +void helper_mtc0_config5(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_Config5 = (env->CP0_Config5 & (~env->CP0_Config5_rw_bitmask)) | +                       (arg1 & env->CP0_Config5_rw_bitmask); +    compute_hflags(env); +} + +void helper_mtc0_lladdr(CPUMIPSState *env, target_ulong arg1) +{ +    target_long mask = env->CP0_LLAddr_rw_bitmask; +    arg1 = arg1 << env->CP0_LLAddr_shift; +    env->lladdr = (env->lladdr & ~mask) | (arg1 & mask); +} + +void helper_mtc0_watchlo(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ +    /* Watch exceptions for instructions, data loads, data stores +       not implemented. */ +    env->CP0_WatchLo[sel] = (arg1 & ~0x7); +} + +void helper_mtc0_watchhi(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ +    env->CP0_WatchHi[sel] = (arg1 & 0x40FF0FF8); +    env->CP0_WatchHi[sel] &= ~(env->CP0_WatchHi[sel] & arg1 & 0x7); +} + +void helper_mtc0_xcontext(CPUMIPSState *env, target_ulong arg1) +{ +    target_ulong mask = (1ULL << (env->SEGBITS - 7)) - 1; +    env->CP0_XContext = (env->CP0_XContext & mask) | (arg1 & ~mask); +} + +void helper_mtc0_framemask(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_Framemask = arg1; /* XXX */ +} + +void helper_mtc0_debug(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_Debug = (env->CP0_Debug & 0x8C03FC1F) | (arg1 & 0x13300120); +    if (arg1 & (1 << CP0DB_DM)) +        env->hflags |= MIPS_HFLAG_DM; +    else +        env->hflags &= ~MIPS_HFLAG_DM; +} + +void helper_mttc0_debug(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    uint32_t val = arg1 & ((1 << CP0DB_SSt) | (1 << CP0DB_Halt)); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    /* XXX: Might be wrong, check with EJTAG spec. */ +    if (other_tc == other->current_tc) +        other->active_tc.CP0_Debug_tcstatus = val; +    else +        other->tcs[other_tc].CP0_Debug_tcstatus = val; +    other->CP0_Debug = (other->CP0_Debug & +                     ((1 << CP0DB_SSt) | (1 << CP0DB_Halt))) | +                     (arg1 & ~((1 << CP0DB_SSt) | (1 << CP0DB_Halt))); +} + +void helper_mtc0_performance0(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_Performance0 = arg1 & 0x000007ff; +} + +void helper_mtc0_taglo(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_TagLo = arg1 & 0xFFFFFCF6; +} + +void helper_mtc0_datalo(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_DataLo = arg1; /* XXX */ +} + +void helper_mtc0_taghi(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_TagHi = arg1; /* XXX */ +} + +void helper_mtc0_datahi(CPUMIPSState *env, target_ulong arg1) +{ +    env->CP0_DataHi = arg1; /* XXX */ +} + +/* MIPS MT functions */ +target_ulong helper_mftgpr(CPUMIPSState *env, uint32_t sel) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.gpr[sel]; +    else +        return other->tcs[other_tc].gpr[sel]; +} + +target_ulong helper_mftlo(CPUMIPSState *env, uint32_t sel) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.LO[sel]; +    else +        return other->tcs[other_tc].LO[sel]; +} + +target_ulong helper_mfthi(CPUMIPSState *env, uint32_t sel) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.HI[sel]; +    else +        return other->tcs[other_tc].HI[sel]; +} + +target_ulong helper_mftacx(CPUMIPSState *env, uint32_t sel) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.ACX[sel]; +    else +        return other->tcs[other_tc].ACX[sel]; +} + +target_ulong helper_mftdsp(CPUMIPSState *env) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        return other->active_tc.DSPControl; +    else +        return other->tcs[other_tc].DSPControl; +} + +void helper_mttgpr(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        other->active_tc.gpr[sel] = arg1; +    else +        other->tcs[other_tc].gpr[sel] = arg1; +} + +void helper_mttlo(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        other->active_tc.LO[sel] = arg1; +    else +        other->tcs[other_tc].LO[sel] = arg1; +} + +void helper_mtthi(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        other->active_tc.HI[sel] = arg1; +    else +        other->tcs[other_tc].HI[sel] = arg1; +} + +void helper_mttacx(CPUMIPSState *env, target_ulong arg1, uint32_t sel) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        other->active_tc.ACX[sel] = arg1; +    else +        other->tcs[other_tc].ACX[sel] = arg1; +} + +void helper_mttdsp(CPUMIPSState *env, target_ulong arg1) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    CPUMIPSState *other = mips_cpu_map_tc(env, &other_tc); + +    if (other_tc == other->current_tc) +        other->active_tc.DSPControl = arg1; +    else +        other->tcs[other_tc].DSPControl = arg1; +} + +/* MIPS MT functions */ +target_ulong helper_dmt(void) +{ +    // TODO +     return 0; +} + +target_ulong helper_emt(void) +{ +    // TODO +    return 0; +} + +target_ulong helper_dvpe(CPUMIPSState *env) +{ +    CPUState *other_cs = first_cpu; +    target_ulong prev = env->mvp->CP0_MVPControl; + +    CPU_FOREACH(other_cs) { +        MIPSCPU *other_cpu = MIPS_CPU(other_cs); +        /* Turn off all VPEs except the one executing the dvpe.  */ +        if (&other_cpu->env != env) { +            other_cpu->env.mvp->CP0_MVPControl &= ~(1 << CP0MVPCo_EVP); +            mips_vpe_sleep(other_cpu); +        } +    } +    return prev; +} + +target_ulong helper_evpe(CPUMIPSState *env) +{ +    CPUState *other_cs = first_cpu; +    target_ulong prev = env->mvp->CP0_MVPControl; + +    CPU_FOREACH(other_cs) { +        MIPSCPU *other_cpu = MIPS_CPU(other_cs); + +        if (&other_cpu->env != env +            /* If the VPE is WFI, don't disturb its sleep.  */ +            && !mips_vpe_is_wfi(other_cpu)) { +            /* Enable the VPE.  */ +            other_cpu->env.mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP); +            mips_vpe_wake(other_cpu); /* And wake it up.  */ +        } +    } +    return prev; +} +#endif /* !CONFIG_USER_ONLY */ + +void helper_fork(target_ulong arg1, target_ulong arg2) +{ +    // arg1 = rt, arg2 = rs +    // TODO: store to TC register +} + +target_ulong helper_yield(CPUMIPSState *env, target_ulong arg) +{ +    target_long arg1 = arg; + +    if (arg1 < 0) { +        /* No scheduling policy implemented. */ +        if (arg1 != -2) { +            if (env->CP0_VPEControl & (1 << CP0VPECo_YSI) && +                env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) { +                env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); +                env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT; +                helper_raise_exception(env, EXCP_THREAD); +            } +        } +    } else if (arg1 == 0) { +        if (0 /* TODO: TC underflow */) { +            env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); +            helper_raise_exception(env, EXCP_THREAD); +        } else { +            // TODO: Deallocate TC +        } +    } else if (arg1 > 0) { +        /* Yield qualifier inputs not implemented. */ +        env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); +        env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT; +        helper_raise_exception(env, EXCP_THREAD); +    } +    return env->CP0_YQMask; +} + +#ifndef CONFIG_USER_ONLY +/* TLB management */ +static void cpu_mips_tlb_flush (CPUMIPSState *env, int flush_global) +{ +    MIPSCPU *cpu = mips_env_get_cpu(env); + +    /* Flush qemu's TLB and discard all shadowed entries.  */ +    tlb_flush(CPU(cpu), flush_global); +    env->tlb->tlb_in_use = env->tlb->nb_tlb; +} + +static void r4k_mips_tlb_flush_extra (CPUMIPSState *env, int first) +{ +    /* Discard entries from env->tlb[first] onwards.  */ +    while (env->tlb->tlb_in_use > first) { +        r4k_invalidate_tlb(env, --env->tlb->tlb_in_use, 0); +    } +} + +static inline uint64_t get_tlb_pfn_from_entrylo(uint64_t entrylo) +{ +#if defined(TARGET_MIPS64) +    return extract64(entrylo, 6, 54); +#else +    return extract64(entrylo, 6, 24) | /* PFN */ +           (extract64(entrylo, 32, 32) << 24); /* PFNX */ +#endif +} + +static void r4k_fill_tlb(CPUMIPSState *env, int idx) +{ +    r4k_tlb_t *tlb; + +    /* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */ +    tlb = &env->tlb->mmu.r4k.tlb[idx]; +    if (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) { +        tlb->EHINV = 1; +        return; +    } +    tlb->EHINV = 0; +    tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); +#if defined(TARGET_MIPS64) +    tlb->VPN &= env->SEGMask; +#endif +    tlb->ASID = env->CP0_EntryHi & 0xFF; +    tlb->PageMask = env->CP0_PageMask; +    tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; +    tlb->V0 = (env->CP0_EntryLo0 & 2) != 0; +    tlb->D0 = (env->CP0_EntryLo0 & 4) != 0; +    tlb->C0 = (env->CP0_EntryLo0 >> 3) & 0x7; +    tlb->XI0 = (env->CP0_EntryLo0 >> CP0EnLo_XI) & 1; +    tlb->RI0 = (env->CP0_EntryLo0 >> CP0EnLo_RI) & 1; +    tlb->PFN[0] = get_tlb_pfn_from_entrylo(env->CP0_EntryLo0) << 12; +    tlb->V1 = (env->CP0_EntryLo1 & 2) != 0; +    tlb->D1 = (env->CP0_EntryLo1 & 4) != 0; +    tlb->C1 = (env->CP0_EntryLo1 >> 3) & 0x7; +    tlb->XI1 = (env->CP0_EntryLo1 >> CP0EnLo_XI) & 1; +    tlb->RI1 = (env->CP0_EntryLo1 >> CP0EnLo_RI) & 1; +    tlb->PFN[1] = get_tlb_pfn_from_entrylo(env->CP0_EntryLo1) << 12; +} + +void r4k_helper_tlbinv(CPUMIPSState *env) +{ +    int idx; +    r4k_tlb_t *tlb; +    uint8_t ASID = env->CP0_EntryHi & 0xFF; + +    for (idx = 0; idx < env->tlb->nb_tlb; idx++) { +        tlb = &env->tlb->mmu.r4k.tlb[idx]; +        if (!tlb->G && tlb->ASID == ASID) { +            tlb->EHINV = 1; +        } +    } +    cpu_mips_tlb_flush(env, 1); +} + +void r4k_helper_tlbinvf(CPUMIPSState *env) +{ +    int idx; + +    for (idx = 0; idx < env->tlb->nb_tlb; idx++) { +        env->tlb->mmu.r4k.tlb[idx].EHINV = 1; +    } +    cpu_mips_tlb_flush(env, 1); +} + +void r4k_helper_tlbwi(CPUMIPSState *env) +{ +    r4k_tlb_t *tlb; +    int idx; +    target_ulong VPN; +    uint8_t ASID; +    bool G, V0, D0, V1, D1; + +    idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; +    tlb = &env->tlb->mmu.r4k.tlb[idx]; +    VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1); +#if defined(TARGET_MIPS64) +    VPN &= env->SEGMask; +#endif +    ASID = env->CP0_EntryHi & 0xff; +    G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1; +    V0 = (env->CP0_EntryLo0 & 2) != 0; +    D0 = (env->CP0_EntryLo0 & 4) != 0; +    V1 = (env->CP0_EntryLo1 & 2) != 0; +    D1 = (env->CP0_EntryLo1 & 4) != 0; + +    /* Discard cached TLB entries, unless tlbwi is just upgrading access +       permissions on the current entry. */ +    if (tlb->VPN != VPN || tlb->ASID != ASID || tlb->G != G || +        (tlb->V0 && !V0) || (tlb->D0 && !D0) || +        (tlb->V1 && !V1) || (tlb->D1 && !D1)) { +        r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); +    } + +    r4k_invalidate_tlb(env, idx, 0); +    r4k_fill_tlb(env, idx); +} + +void r4k_helper_tlbwr(CPUMIPSState *env) +{ +    int r = cpu_mips_get_random(env); + +    r4k_invalidate_tlb(env, r, 1); +    r4k_fill_tlb(env, r); +} + +void r4k_helper_tlbp(CPUMIPSState *env) +{ +    r4k_tlb_t *tlb; +    target_ulong mask; +    target_ulong tag; +    target_ulong VPN; +    uint8_t ASID; +    int i; + +    ASID = env->CP0_EntryHi & 0xFF; +    for (i = 0; i < env->tlb->nb_tlb; i++) { +        tlb = &env->tlb->mmu.r4k.tlb[i]; +        /* 1k pages are not supported. */ +        mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); +        tag = env->CP0_EntryHi & ~mask; +        VPN = tlb->VPN & ~mask; +#if defined(TARGET_MIPS64) +        tag &= env->SEGMask; +#endif +        /* Check ASID, virtual page number & size */ +        if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag && !tlb->EHINV) { +            /* TLB match */ +            env->CP0_Index = i; +            break; +        } +    } +    if (i == env->tlb->nb_tlb) { +        /* No match.  Discard any shadow entries, if any of them match.  */ +        for (i = env->tlb->nb_tlb; i < env->tlb->tlb_in_use; i++) { +            tlb = &env->tlb->mmu.r4k.tlb[i]; +            /* 1k pages are not supported. */ +            mask = tlb->PageMask | ~(TARGET_PAGE_MASK << 1); +            tag = env->CP0_EntryHi & ~mask; +            VPN = tlb->VPN & ~mask; +#if defined(TARGET_MIPS64) +            tag &= env->SEGMask; +#endif +            /* Check ASID, virtual page number & size */ +            if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) { +                r4k_mips_tlb_flush_extra (env, i); +                break; +            } +        } + +        env->CP0_Index |= 0x80000000; +    } +} + +static inline uint64_t get_entrylo_pfn_from_tlb(uint64_t tlb_pfn) +{ +#if defined(TARGET_MIPS64) +    return tlb_pfn << 6; +#else +    return (extract64(tlb_pfn, 0, 24) << 6) | /* PFN */ +           (extract64(tlb_pfn, 24, 32) << 32); /* PFNX */ +#endif +} + +void r4k_helper_tlbr(CPUMIPSState *env) +{ +    r4k_tlb_t *tlb; +    uint8_t ASID; +    int idx; + +    ASID = env->CP0_EntryHi & 0xFF; +    idx = (env->CP0_Index & ~0x80000000) % env->tlb->nb_tlb; +    tlb = &env->tlb->mmu.r4k.tlb[idx]; + +    /* If this will change the current ASID, flush qemu's TLB.  */ +    if (ASID != tlb->ASID) +        cpu_mips_tlb_flush (env, 1); + +    r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb); + +    if (tlb->EHINV) { +        env->CP0_EntryHi = 1 << CP0EnHi_EHINV; +        env->CP0_PageMask = 0; +        env->CP0_EntryLo0 = 0; +        env->CP0_EntryLo1 = 0; +    } else { +        env->CP0_EntryHi = tlb->VPN | tlb->ASID; +        env->CP0_PageMask = tlb->PageMask; +        env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) | +                        ((uint64_t)tlb->RI0 << CP0EnLo_RI) | +                        ((uint64_t)tlb->XI0 << CP0EnLo_XI) | (tlb->C0 << 3) | +                        get_entrylo_pfn_from_tlb(tlb->PFN[0] >> 12); +        env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) | +                        ((uint64_t)tlb->RI1 << CP0EnLo_RI) | +                        ((uint64_t)tlb->XI1 << CP0EnLo_XI) | (tlb->C1 << 3) | +                        get_entrylo_pfn_from_tlb(tlb->PFN[1] >> 12); +    } +} + +void helper_tlbwi(CPUMIPSState *env) +{ +    env->tlb->helper_tlbwi(env); +} + +void helper_tlbwr(CPUMIPSState *env) +{ +    env->tlb->helper_tlbwr(env); +} + +void helper_tlbp(CPUMIPSState *env) +{ +    env->tlb->helper_tlbp(env); +} + +void helper_tlbr(CPUMIPSState *env) +{ +    env->tlb->helper_tlbr(env); +} + +void helper_tlbinv(CPUMIPSState *env) +{ +    env->tlb->helper_tlbinv(env); +} + +void helper_tlbinvf(CPUMIPSState *env) +{ +    env->tlb->helper_tlbinvf(env); +} + +/* Specials */ +target_ulong helper_di(CPUMIPSState *env) +{ +    target_ulong t0 = env->CP0_Status; + +    env->CP0_Status = t0 & ~(1 << CP0St_IE); +    return t0; +} + +target_ulong helper_ei(CPUMIPSState *env) +{ +    target_ulong t0 = env->CP0_Status; + +    env->CP0_Status = t0 | (1 << CP0St_IE); +    return t0; +} + +static void debug_pre_eret(CPUMIPSState *env) +{ +    if (qemu_loglevel_mask(CPU_LOG_EXEC)) { +        qemu_log("ERET: PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx, +                env->active_tc.PC, env->CP0_EPC); +        if (env->CP0_Status & (1 << CP0St_ERL)) +            qemu_log(" ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC); +        if (env->hflags & MIPS_HFLAG_DM) +            qemu_log(" DEPC " TARGET_FMT_lx, env->CP0_DEPC); +        qemu_log("\n"); +    } +} + +static void debug_post_eret(CPUMIPSState *env) +{ +    MIPSCPU *cpu = mips_env_get_cpu(env); + +    if (qemu_loglevel_mask(CPU_LOG_EXEC)) { +        qemu_log("  =>  PC " TARGET_FMT_lx " EPC " TARGET_FMT_lx, +                env->active_tc.PC, env->CP0_EPC); +        if (env->CP0_Status & (1 << CP0St_ERL)) +            qemu_log(" ErrorEPC " TARGET_FMT_lx, env->CP0_ErrorEPC); +        if (env->hflags & MIPS_HFLAG_DM) +            qemu_log(" DEPC " TARGET_FMT_lx, env->CP0_DEPC); +        switch (env->hflags & MIPS_HFLAG_KSU) { +        case MIPS_HFLAG_UM: qemu_log(", UM\n"); break; +        case MIPS_HFLAG_SM: qemu_log(", SM\n"); break; +        case MIPS_HFLAG_KM: qemu_log("\n"); break; +        default: +            cpu_abort(CPU(cpu), "Invalid MMU mode!\n"); +            break; +        } +    } +} + +static void set_pc(CPUMIPSState *env, target_ulong error_pc) +{ +    env->active_tc.PC = error_pc & ~(target_ulong)1; +    if (error_pc & 1) { +        env->hflags |= MIPS_HFLAG_M16; +    } else { +        env->hflags &= ~(MIPS_HFLAG_M16); +    } +} + +static inline void exception_return(CPUMIPSState *env) +{ +    debug_pre_eret(env); +    if (env->CP0_Status & (1 << CP0St_ERL)) { +        set_pc(env, env->CP0_ErrorEPC); +        env->CP0_Status &= ~(1 << CP0St_ERL); +    } else { +        set_pc(env, env->CP0_EPC); +        env->CP0_Status &= ~(1 << CP0St_EXL); +    } +    compute_hflags(env); +    debug_post_eret(env); +} + +void helper_eret(CPUMIPSState *env) +{ +    exception_return(env); +    env->lladdr = 1; +} + +void helper_eretnc(CPUMIPSState *env) +{ +    exception_return(env); +} + +void helper_deret(CPUMIPSState *env) +{ +    debug_pre_eret(env); +    set_pc(env, env->CP0_DEPC); + +    env->hflags &= ~MIPS_HFLAG_DM; +    compute_hflags(env); +    debug_post_eret(env); +} +#endif /* !CONFIG_USER_ONLY */ + +target_ulong helper_rdhwr_cpunum(CPUMIPSState *env) +{ +    if ((env->hflags & MIPS_HFLAG_CP0) || +        (env->CP0_HWREna & (1 << 0))) +        return env->CP0_EBase & 0x3ff; +    else +        helper_raise_exception(env, EXCP_RI); + +    return 0; +} + +target_ulong helper_rdhwr_synci_step(CPUMIPSState *env) +{ +    if ((env->hflags & MIPS_HFLAG_CP0) || +        (env->CP0_HWREna & (1 << 1))) +        return env->SYNCI_Step; +    else +        helper_raise_exception(env, EXCP_RI); + +    return 0; +} + +target_ulong helper_rdhwr_cc(CPUMIPSState *env) +{ +    if ((env->hflags & MIPS_HFLAG_CP0) || +        (env->CP0_HWREna & (1 << 2))) +        return env->CP0_Count; +    else +        helper_raise_exception(env, EXCP_RI); + +    return 0; +} + +target_ulong helper_rdhwr_ccres(CPUMIPSState *env) +{ +    if ((env->hflags & MIPS_HFLAG_CP0) || +        (env->CP0_HWREna & (1 << 3))) +        return env->CCRes; +    else +        helper_raise_exception(env, EXCP_RI); + +    return 0; +} + +void helper_pmon(CPUMIPSState *env, int function) +{ +    function /= 2; +    switch (function) { +    case 2: /* TODO: char inbyte(int waitflag); */ +        if (env->active_tc.gpr[4] == 0) +            env->active_tc.gpr[2] = -1; +        /* Fall through */ +    case 11: /* TODO: char inbyte (void); */ +        env->active_tc.gpr[2] = -1; +        break; +    case 3: +    case 12: +        printf("%c", (char)(env->active_tc.gpr[4] & 0xFF)); +        break; +    case 17: +        break; +    case 158: +        { +            unsigned char *fmt = (void *)(uintptr_t)env->active_tc.gpr[4]; +            printf("%s", fmt); +        } +        break; +    } +} + +void helper_wait(CPUMIPSState *env) +{ +    CPUState *cs = CPU(mips_env_get_cpu(env)); + +    cs->halted = 1; +    cpu_reset_interrupt(cs, CPU_INTERRUPT_WAKE); +    helper_raise_exception(env, EXCP_HLT); +} + +#if !defined(CONFIG_USER_ONLY) + +void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr, +                                  int access_type, int is_user, +                                  uintptr_t retaddr) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    int error_code = 0; +    int excp; + +    env->CP0_BadVAddr = addr; + +    if (access_type == MMU_DATA_STORE) { +        excp = EXCP_AdES; +    } else { +        excp = EXCP_AdEL; +        if (access_type == MMU_INST_FETCH) { +            error_code |= EXCP_INST_NOTAVAIL; +        } +    } + +    do_raise_exception_err(env, excp, error_code, retaddr); +} + +void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, +              uintptr_t retaddr) +{ +    int ret; + +    ret = mips_cpu_handle_mmu_fault(cs, addr, is_write, mmu_idx); +    if (ret) { +        MIPSCPU *cpu = MIPS_CPU(cs); +        CPUMIPSState *env = &cpu->env; + +        do_raise_exception_err(env, cs->exception_index, +                               env->error_code, retaddr); +    } +} + +void mips_cpu_unassigned_access(CPUState *cs, hwaddr addr, +                                bool is_write, bool is_exec, int unused, +                                unsigned size) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; + +    /* +     * Raising an exception with KVM enabled will crash because it won't be from +     * the main execution loop so the longjmp won't have a matching setjmp. +     * Until we can trigger a bus error exception through KVM lets just ignore +     * the access. +     */ +    if (kvm_enabled()) { +        return; +    } + +    if (is_exec) { +        helper_raise_exception(env, EXCP_IBE); +    } else { +        helper_raise_exception(env, EXCP_DBE); +    } +} +#endif /* !CONFIG_USER_ONLY */ + +/* Complex FPU operations which may need stack space. */ + +#define FLOAT_TWO32 make_float32(1 << 30) +#define FLOAT_TWO64 make_float64(1ULL << 62) +#define FP_TO_INT32_OVERFLOW 0x7fffffff +#define FP_TO_INT64_OVERFLOW 0x7fffffffffffffffULL + +/* convert MIPS rounding mode in FCR31 to IEEE library */ +unsigned int ieee_rm[] = { +    float_round_nearest_even, +    float_round_to_zero, +    float_round_up, +    float_round_down +}; + +target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg) +{ +    target_ulong arg1 = 0; + +    switch (reg) { +    case 0: +        arg1 = (int32_t)env->active_fpu.fcr0; +        break; +    case 1: +        /* UFR Support - Read Status FR */ +        if (env->active_fpu.fcr0 & (1 << FCR0_UFRP)) { +            if (env->CP0_Config5 & (1 << CP0C5_UFR)) { +                arg1 = (int32_t) +                       ((env->CP0_Status & (1  << CP0St_FR)) >> CP0St_FR); +            } else { +                helper_raise_exception(env, EXCP_RI); +            } +        } +        break; +    case 5: +        /* FRE Support - read Config5.FRE bit */ +        if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { +            if (env->CP0_Config5 & (1 << CP0C5_UFE)) { +                arg1 = (env->CP0_Config5 >> CP0C5_FRE) & 1; +            } else { +                helper_raise_exception(env, EXCP_RI); +            } +        } +        break; +    case 25: +        arg1 = ((env->active_fpu.fcr31 >> 24) & 0xfe) | ((env->active_fpu.fcr31 >> 23) & 0x1); +        break; +    case 26: +        arg1 = env->active_fpu.fcr31 & 0x0003f07c; +        break; +    case 28: +        arg1 = (env->active_fpu.fcr31 & 0x00000f83) | ((env->active_fpu.fcr31 >> 22) & 0x4); +        break; +    default: +        arg1 = (int32_t)env->active_fpu.fcr31; +        break; +    } + +    return arg1; +} + +void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) +{ +    switch (fs) { +    case 1: +        /* UFR Alias - Reset Status FR */ +        if (!((env->active_fpu.fcr0 & (1 << FCR0_UFRP)) && (rt == 0))) { +            return; +        } +        if (env->CP0_Config5 & (1 << CP0C5_UFR)) { +            env->CP0_Status &= ~(1 << CP0St_FR); +            compute_hflags(env); +        } else { +            helper_raise_exception(env, EXCP_RI); +        } +        break; +    case 4: +        /* UNFR Alias - Set Status FR */ +        if (!((env->active_fpu.fcr0 & (1 << FCR0_UFRP)) && (rt == 0))) { +            return; +        } +        if (env->CP0_Config5 & (1 << CP0C5_UFR)) { +            env->CP0_Status |= (1 << CP0St_FR); +            compute_hflags(env); +        } else { +            helper_raise_exception(env, EXCP_RI); +        } +        break; +    case 5: +        /* FRE Support - clear Config5.FRE bit */ +        if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) { +            return; +        } +        if (env->CP0_Config5 & (1 << CP0C5_UFE)) { +            env->CP0_Config5 &= ~(1 << CP0C5_FRE); +            compute_hflags(env); +        } else { +            helper_raise_exception(env, EXCP_RI); +        } +        break; +    case 6: +        /* FRE Support - set Config5.FRE bit */ +        if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) { +            return; +        } +        if (env->CP0_Config5 & (1 << CP0C5_UFE)) { +            env->CP0_Config5 |= (1 << CP0C5_FRE); +            compute_hflags(env); +        } else { +            helper_raise_exception(env, EXCP_RI); +        } +        break; +    case 25: +        if ((env->insn_flags & ISA_MIPS32R6) || (arg1 & 0xffffff00)) { +            return; +        } +        env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0x017fffff) | ((arg1 & 0xfe) << 24) | +                     ((arg1 & 0x1) << 23); +        break; +    case 26: +        if (arg1 & 0x007c0000) +            return; +        env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0xfffc0f83) | (arg1 & 0x0003f07c); +        break; +    case 28: +        if (arg1 & 0x007c0000) +            return; +        env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0xfefff07c) | (arg1 & 0x00000f83) | +                     ((arg1 & 0x4) << 22); +        break; +    case 31: +        if (env->insn_flags & ISA_MIPS32R6) { +            uint32_t mask = 0xfefc0000; +            env->active_fpu.fcr31 = (arg1 & ~mask) | +                (env->active_fpu.fcr31 & mask); +        } else if (!(arg1 & 0x007c0000)) { +            env->active_fpu.fcr31 = arg1; +        } +        break; +    default: +        return; +    } +    /* set rounding mode */ +    restore_rounding_mode(env); +    /* set flush-to-zero mode */ +    restore_flush_mode(env); +    set_float_exception_flags(0, &env->active_fpu.fp_status); +    if ((GET_FP_ENABLE(env->active_fpu.fcr31) | 0x20) & GET_FP_CAUSE(env->active_fpu.fcr31)) +        do_raise_exception(env, EXCP_FPE, GETPC()); +} + +int ieee_ex_to_mips(int xcpt) +{ +    int ret = 0; +    if (xcpt) { +        if (xcpt & float_flag_invalid) { +            ret |= FP_INVALID; +        } +        if (xcpt & float_flag_overflow) { +            ret |= FP_OVERFLOW; +        } +        if (xcpt & float_flag_underflow) { +            ret |= FP_UNDERFLOW; +        } +        if (xcpt & float_flag_divbyzero) { +            ret |= FP_DIV0; +        } +        if (xcpt & float_flag_inexact) { +            ret |= FP_INEXACT; +        } +    } +    return ret; +} + +static inline void update_fcr31(CPUMIPSState *env, uintptr_t pc) +{ +    int tmp = ieee_ex_to_mips(get_float_exception_flags(&env->active_fpu.fp_status)); + +    SET_FP_CAUSE(env->active_fpu.fcr31, tmp); + +    if (tmp) { +        set_float_exception_flags(0, &env->active_fpu.fp_status); + +        if (GET_FP_ENABLE(env->active_fpu.fcr31) & tmp) { +            do_raise_exception(env, EXCP_FPE, pc); +        } else { +            UPDATE_FP_FLAGS(env->active_fpu.fcr31, tmp); +        } +    } +} + +/* Float support. +   Single precition routines have a "s" suffix, double precision a +   "d" suffix, 32bit integer "w", 64bit integer "l", paired single "ps", +   paired single lower "pl", paired single upper "pu".  */ + +/* unary operations, modifying fp status  */ +uint64_t helper_float_sqrt_d(CPUMIPSState *env, uint64_t fdt0) +{ +    fdt0 = float64_sqrt(fdt0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fdt0; +} + +uint32_t helper_float_sqrt_s(CPUMIPSState *env, uint32_t fst0) +{ +    fst0 = float32_sqrt(fst0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fst0; +} + +uint64_t helper_float_cvtd_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint64_t fdt2; + +    fdt2 = float32_to_float64(fst0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fdt2; +} + +uint64_t helper_float_cvtd_w(CPUMIPSState *env, uint32_t wt0) +{ +    uint64_t fdt2; + +    fdt2 = int32_to_float64(wt0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fdt2; +} + +uint64_t helper_float_cvtd_l(CPUMIPSState *env, uint64_t dt0) +{ +    uint64_t fdt2; + +    fdt2 = int64_to_float64(dt0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fdt2; +} + +uint64_t helper_float_cvtl_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint64_t dt2; + +    dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        dt2 = FP_TO_INT64_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return dt2; +} + +uint64_t helper_float_cvtl_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint64_t dt2; + +    dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        dt2 = FP_TO_INT64_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return dt2; +} + +uint64_t helper_float_cvtps_pw(CPUMIPSState *env, uint64_t dt0) +{ +    uint32_t fst2; +    uint32_t fsth2; + +    fst2 = int32_to_float32(dt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); +    fsth2 = int32_to_float32(dt0 >> 32, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return ((uint64_t)fsth2 << 32) | fst2; +} + +uint64_t helper_float_cvtpw_ps(CPUMIPSState *env, uint64_t fdt0) +{ +    uint32_t wt2; +    uint32_t wth2; +    int excp, excph; + +    wt2 = float32_to_int32(fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); +    excp = get_float_exception_flags(&env->active_fpu.fp_status); +    if (excp & (float_flag_overflow | float_flag_invalid)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } + +    set_float_exception_flags(0, &env->active_fpu.fp_status); +    wth2 = float32_to_int32(fdt0 >> 32, &env->active_fpu.fp_status); +    excph = get_float_exception_flags(&env->active_fpu.fp_status); +    if (excph & (float_flag_overflow | float_flag_invalid)) { +        wth2 = FP_TO_INT32_OVERFLOW; +    } + +    set_float_exception_flags(excp | excph, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); + +    return ((uint64_t)wth2 << 32) | wt2; +} + +uint32_t helper_float_cvts_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint32_t fst2; + +    fst2 = float64_to_float32(fdt0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fst2; +} + +uint32_t helper_float_cvts_w(CPUMIPSState *env, uint32_t wt0) +{ +    uint32_t fst2; + +    fst2 = int32_to_float32(wt0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fst2; +} + +uint32_t helper_float_cvts_l(CPUMIPSState *env, uint64_t dt0) +{ +    uint32_t fst2; + +    fst2 = int64_to_float32(dt0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fst2; +} + +uint32_t helper_float_cvts_pl(CPUMIPSState *env, uint32_t wt0) +{ +    uint32_t wt2; + +    wt2 = wt0; +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint32_t helper_float_cvts_pu(CPUMIPSState *env, uint32_t wth0) +{ +    uint32_t wt2; + +    wt2 = wth0; +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint32_t helper_float_cvtw_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint32_t wt2; + +    wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint32_t helper_float_cvtw_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint32_t wt2; + +    wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint64_t helper_float_roundl_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint64_t dt2; + +    set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); +    dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        dt2 = FP_TO_INT64_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return dt2; +} + +uint64_t helper_float_roundl_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint64_t dt2; + +    set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); +    dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        dt2 = FP_TO_INT64_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return dt2; +} + +uint32_t helper_float_roundw_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint32_t wt2; + +    set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); +    wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint32_t helper_float_roundw_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint32_t wt2; + +    set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status); +    wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint64_t helper_float_truncl_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint64_t dt2; + +    dt2 = float64_to_int64_round_to_zero(fdt0, &env->active_fpu.fp_status); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        dt2 = FP_TO_INT64_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return dt2; +} + +uint64_t helper_float_truncl_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint64_t dt2; + +    dt2 = float32_to_int64_round_to_zero(fst0, &env->active_fpu.fp_status); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        dt2 = FP_TO_INT64_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return dt2; +} + +uint32_t helper_float_truncw_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint32_t wt2; + +    wt2 = float64_to_int32_round_to_zero(fdt0, &env->active_fpu.fp_status); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint32_t helper_float_truncw_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint32_t wt2; + +    wt2 = float32_to_int32_round_to_zero(fst0, &env->active_fpu.fp_status); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint64_t helper_float_ceill_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint64_t dt2; + +    set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); +    dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        dt2 = FP_TO_INT64_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return dt2; +} + +uint64_t helper_float_ceill_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint64_t dt2; + +    set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); +    dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        dt2 = FP_TO_INT64_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return dt2; +} + +uint32_t helper_float_ceilw_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint32_t wt2; + +    set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); +    wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint32_t helper_float_ceilw_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint32_t wt2; + +    set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status); +    wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint64_t helper_float_floorl_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint64_t dt2; + +    set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); +    dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        dt2 = FP_TO_INT64_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return dt2; +} + +uint64_t helper_float_floorl_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint64_t dt2; + +    set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); +    dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        dt2 = FP_TO_INT64_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return dt2; +} + +uint32_t helper_float_floorw_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint32_t wt2; + +    set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); +    wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return wt2; +} + +uint32_t helper_float_floorw_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint32_t wt2; + +    set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status); +    wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status); +    restore_rounding_mode(env); +    if (get_float_exception_flags(&env->active_fpu.fp_status) +        & (float_flag_invalid | float_flag_overflow)) { +        wt2 = FP_TO_INT32_OVERFLOW; +    } +    update_fcr31(env, GETPC()); +    return wt2; +} + +/* unary operations, not modifying fp status  */ +#define FLOAT_UNOP(name)                                       \ +uint64_t helper_float_ ## name ## _d(uint64_t fdt0)                \ +{                                                              \ +    return float64_ ## name(fdt0);                             \ +}                                                              \ +uint32_t helper_float_ ## name ## _s(uint32_t fst0)                \ +{                                                              \ +    return float32_ ## name(fst0);                             \ +}                                                              \ +uint64_t helper_float_ ## name ## _ps(uint64_t fdt0)               \ +{                                                              \ +    uint32_t wt0;                                              \ +    uint32_t wth0;                                             \ +                                                               \ +    wt0 = float32_ ## name(fdt0 & 0XFFFFFFFF);                 \ +    wth0 = float32_ ## name(fdt0 >> 32);                       \ +    return ((uint64_t)wth0 << 32) | wt0;                       \ +} +FLOAT_UNOP(abs) +FLOAT_UNOP(chs) +#undef FLOAT_UNOP + +/* MIPS specific unary operations */ +uint64_t helper_float_recip_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint64_t fdt2; + +    fdt2 = float64_div(float64_one, fdt0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fdt2; +} + +uint32_t helper_float_recip_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint32_t fst2; + +    fst2 = float32_div(float32_one, fst0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fst2; +} + +uint64_t helper_float_rsqrt_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint64_t fdt2; + +    fdt2 = float64_sqrt(fdt0, &env->active_fpu.fp_status); +    fdt2 = float64_div(float64_one, fdt2, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fdt2; +} + +uint32_t helper_float_rsqrt_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint32_t fst2; + +    fst2 = float32_sqrt(fst0, &env->active_fpu.fp_status); +    fst2 = float32_div(float32_one, fst2, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fst2; +} + +uint64_t helper_float_recip1_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint64_t fdt2; + +    fdt2 = float64_div(float64_one, fdt0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fdt2; +} + +uint32_t helper_float_recip1_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint32_t fst2; + +    fst2 = float32_div(float32_one, fst0, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fst2; +} + +uint64_t helper_float_recip1_ps(CPUMIPSState *env, uint64_t fdt0) +{ +    uint32_t fst2; +    uint32_t fsth2; + +    fst2 = float32_div(float32_one, fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); +    fsth2 = float32_div(float32_one, fdt0 >> 32, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return ((uint64_t)fsth2 << 32) | fst2; +} + +uint64_t helper_float_rsqrt1_d(CPUMIPSState *env, uint64_t fdt0) +{ +    uint64_t fdt2; + +    fdt2 = float64_sqrt(fdt0, &env->active_fpu.fp_status); +    fdt2 = float64_div(float64_one, fdt2, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fdt2; +} + +uint32_t helper_float_rsqrt1_s(CPUMIPSState *env, uint32_t fst0) +{ +    uint32_t fst2; + +    fst2 = float32_sqrt(fst0, &env->active_fpu.fp_status); +    fst2 = float32_div(float32_one, fst2, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return fst2; +} + +uint64_t helper_float_rsqrt1_ps(CPUMIPSState *env, uint64_t fdt0) +{ +    uint32_t fst2; +    uint32_t fsth2; + +    fst2 = float32_sqrt(fdt0 & 0XFFFFFFFF, &env->active_fpu.fp_status); +    fsth2 = float32_sqrt(fdt0 >> 32, &env->active_fpu.fp_status); +    fst2 = float32_div(float32_one, fst2, &env->active_fpu.fp_status); +    fsth2 = float32_div(float32_one, fsth2, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return ((uint64_t)fsth2 << 32) | fst2; +} + +#define FLOAT_RINT(name, bits)                                              \ +uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env,                \ +                                          uint ## bits ## _t fs)            \ +{                                                                           \ +    uint ## bits ## _t fdret;                                               \ +                                                                            \ +    fdret = float ## bits ## _round_to_int(fs, &env->active_fpu.fp_status); \ +    update_fcr31(env, GETPC());                                             \ +    return fdret;                                                           \ +} + +FLOAT_RINT(rint_s, 32) +FLOAT_RINT(rint_d, 64) +#undef FLOAT_RINT + +#define FLOAT_CLASS_SIGNALING_NAN      0x001 +#define FLOAT_CLASS_QUIET_NAN          0x002 +#define FLOAT_CLASS_NEGATIVE_INFINITY  0x004 +#define FLOAT_CLASS_NEGATIVE_NORMAL    0x008 +#define FLOAT_CLASS_NEGATIVE_SUBNORMAL 0x010 +#define FLOAT_CLASS_NEGATIVE_ZERO      0x020 +#define FLOAT_CLASS_POSITIVE_INFINITY  0x040 +#define FLOAT_CLASS_POSITIVE_NORMAL    0x080 +#define FLOAT_CLASS_POSITIVE_SUBNORMAL 0x100 +#define FLOAT_CLASS_POSITIVE_ZERO      0x200 + +#define FLOAT_CLASS(name, bits)                                      \ +uint ## bits ## _t helper_float_ ## name (uint ## bits ## _t arg)    \ +{                                                                    \ +    if (float ## bits ## _is_signaling_nan(arg)) {                   \ +        return FLOAT_CLASS_SIGNALING_NAN;                            \ +    } else if (float ## bits ## _is_quiet_nan(arg)) {                \ +        return FLOAT_CLASS_QUIET_NAN;                                \ +    } else if (float ## bits ## _is_neg(arg)) {                      \ +        if (float ## bits ## _is_infinity(arg)) {                    \ +            return FLOAT_CLASS_NEGATIVE_INFINITY;                    \ +        } else if (float ## bits ## _is_zero(arg)) {                 \ +            return FLOAT_CLASS_NEGATIVE_ZERO;                        \ +        } else if (float ## bits ## _is_zero_or_denormal(arg)) {     \ +            return FLOAT_CLASS_NEGATIVE_SUBNORMAL;                   \ +        } else {                                                     \ +            return FLOAT_CLASS_NEGATIVE_NORMAL;                      \ +        }                                                            \ +    } else {                                                         \ +        if (float ## bits ## _is_infinity(arg)) {                    \ +            return FLOAT_CLASS_POSITIVE_INFINITY;                    \ +        } else if (float ## bits ## _is_zero(arg)) {                 \ +            return FLOAT_CLASS_POSITIVE_ZERO;                        \ +        } else if (float ## bits ## _is_zero_or_denormal(arg)) {     \ +            return FLOAT_CLASS_POSITIVE_SUBNORMAL;                   \ +        } else {                                                     \ +            return FLOAT_CLASS_POSITIVE_NORMAL;                      \ +        }                                                            \ +    }                                                                \ +} + +FLOAT_CLASS(class_s, 32) +FLOAT_CLASS(class_d, 64) +#undef FLOAT_CLASS + +/* binary operations */ +#define FLOAT_BINOP(name)                                          \ +uint64_t helper_float_ ## name ## _d(CPUMIPSState *env,            \ +                                     uint64_t fdt0, uint64_t fdt1) \ +{                                                                  \ +    uint64_t dt2;                                                  \ +                                                                   \ +    dt2 = float64_ ## name (fdt0, fdt1, &env->active_fpu.fp_status);     \ +    update_fcr31(env, GETPC());                                    \ +    return dt2;                                                    \ +}                                                                  \ +                                                                   \ +uint32_t helper_float_ ## name ## _s(CPUMIPSState *env,            \ +                                     uint32_t fst0, uint32_t fst1) \ +{                                                                  \ +    uint32_t wt2;                                                  \ +                                                                   \ +    wt2 = float32_ ## name (fst0, fst1, &env->active_fpu.fp_status);     \ +    update_fcr31(env, GETPC());                                    \ +    return wt2;                                                    \ +}                                                                  \ +                                                                   \ +uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env,           \ +                                      uint64_t fdt0,               \ +                                      uint64_t fdt1)               \ +{                                                                  \ +    uint32_t fst0 = fdt0 & 0XFFFFFFFF;                             \ +    uint32_t fsth0 = fdt0 >> 32;                                   \ +    uint32_t fst1 = fdt1 & 0XFFFFFFFF;                             \ +    uint32_t fsth1 = fdt1 >> 32;                                   \ +    uint32_t wt2;                                                  \ +    uint32_t wth2;                                                 \ +                                                                   \ +    wt2 = float32_ ## name (fst0, fst1, &env->active_fpu.fp_status);     \ +    wth2 = float32_ ## name (fsth0, fsth1, &env->active_fpu.fp_status);  \ +    update_fcr31(env, GETPC());                                    \ +    return ((uint64_t)wth2 << 32) | wt2;                           \ +} + +FLOAT_BINOP(add) +FLOAT_BINOP(sub) +FLOAT_BINOP(mul) +FLOAT_BINOP(div) +#undef FLOAT_BINOP + +/* MIPS specific binary operations */ +uint64_t helper_float_recip2_d(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) +{ +    fdt2 = float64_mul(fdt0, fdt2, &env->active_fpu.fp_status); +    fdt2 = float64_chs(float64_sub(fdt2, float64_one, &env->active_fpu.fp_status)); +    update_fcr31(env, GETPC()); +    return fdt2; +} + +uint32_t helper_float_recip2_s(CPUMIPSState *env, uint32_t fst0, uint32_t fst2) +{ +    fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); +    fst2 = float32_chs(float32_sub(fst2, float32_one, &env->active_fpu.fp_status)); +    update_fcr31(env, GETPC()); +    return fst2; +} + +uint64_t helper_float_recip2_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) +{ +    uint32_t fst0 = fdt0 & 0XFFFFFFFF; +    uint32_t fsth0 = fdt0 >> 32; +    uint32_t fst2 = fdt2 & 0XFFFFFFFF; +    uint32_t fsth2 = fdt2 >> 32; + +    fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); +    fsth2 = float32_mul(fsth0, fsth2, &env->active_fpu.fp_status); +    fst2 = float32_chs(float32_sub(fst2, float32_one, &env->active_fpu.fp_status)); +    fsth2 = float32_chs(float32_sub(fsth2, float32_one, &env->active_fpu.fp_status)); +    update_fcr31(env, GETPC()); +    return ((uint64_t)fsth2 << 32) | fst2; +} + +uint64_t helper_float_rsqrt2_d(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) +{ +    fdt2 = float64_mul(fdt0, fdt2, &env->active_fpu.fp_status); +    fdt2 = float64_sub(fdt2, float64_one, &env->active_fpu.fp_status); +    fdt2 = float64_chs(float64_div(fdt2, FLOAT_TWO64, &env->active_fpu.fp_status)); +    update_fcr31(env, GETPC()); +    return fdt2; +} + +uint32_t helper_float_rsqrt2_s(CPUMIPSState *env, uint32_t fst0, uint32_t fst2) +{ +    fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); +    fst2 = float32_sub(fst2, float32_one, &env->active_fpu.fp_status); +    fst2 = float32_chs(float32_div(fst2, FLOAT_TWO32, &env->active_fpu.fp_status)); +    update_fcr31(env, GETPC()); +    return fst2; +} + +uint64_t helper_float_rsqrt2_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt2) +{ +    uint32_t fst0 = fdt0 & 0XFFFFFFFF; +    uint32_t fsth0 = fdt0 >> 32; +    uint32_t fst2 = fdt2 & 0XFFFFFFFF; +    uint32_t fsth2 = fdt2 >> 32; + +    fst2 = float32_mul(fst0, fst2, &env->active_fpu.fp_status); +    fsth2 = float32_mul(fsth0, fsth2, &env->active_fpu.fp_status); +    fst2 = float32_sub(fst2, float32_one, &env->active_fpu.fp_status); +    fsth2 = float32_sub(fsth2, float32_one, &env->active_fpu.fp_status); +    fst2 = float32_chs(float32_div(fst2, FLOAT_TWO32, &env->active_fpu.fp_status)); +    fsth2 = float32_chs(float32_div(fsth2, FLOAT_TWO32, &env->active_fpu.fp_status)); +    update_fcr31(env, GETPC()); +    return ((uint64_t)fsth2 << 32) | fst2; +} + +uint64_t helper_float_addr_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt1) +{ +    uint32_t fst0 = fdt0 & 0XFFFFFFFF; +    uint32_t fsth0 = fdt0 >> 32; +    uint32_t fst1 = fdt1 & 0XFFFFFFFF; +    uint32_t fsth1 = fdt1 >> 32; +    uint32_t fst2; +    uint32_t fsth2; + +    fst2 = float32_add (fst0, fsth0, &env->active_fpu.fp_status); +    fsth2 = float32_add (fst1, fsth1, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return ((uint64_t)fsth2 << 32) | fst2; +} + +uint64_t helper_float_mulr_ps(CPUMIPSState *env, uint64_t fdt0, uint64_t fdt1) +{ +    uint32_t fst0 = fdt0 & 0XFFFFFFFF; +    uint32_t fsth0 = fdt0 >> 32; +    uint32_t fst1 = fdt1 & 0XFFFFFFFF; +    uint32_t fsth1 = fdt1 >> 32; +    uint32_t fst2; +    uint32_t fsth2; + +    fst2 = float32_mul (fst0, fsth0, &env->active_fpu.fp_status); +    fsth2 = float32_mul (fst1, fsth1, &env->active_fpu.fp_status); +    update_fcr31(env, GETPC()); +    return ((uint64_t)fsth2 << 32) | fst2; +} + +#define FLOAT_MINMAX(name, bits, minmaxfunc)                            \ +uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env,            \ +                                          uint ## bits ## _t fs,        \ +                                          uint ## bits ## _t ft)        \ +{                                                                       \ +    uint ## bits ## _t fdret;                                           \ +                                                                        \ +    fdret = float ## bits ## _ ## minmaxfunc(fs, ft,                    \ +                                           &env->active_fpu.fp_status); \ +    update_fcr31(env, GETPC());                                         \ +    return fdret;                                                       \ +} + +FLOAT_MINMAX(max_s, 32, maxnum) +FLOAT_MINMAX(max_d, 64, maxnum) +FLOAT_MINMAX(maxa_s, 32, maxnummag) +FLOAT_MINMAX(maxa_d, 64, maxnummag) + +FLOAT_MINMAX(min_s, 32, minnum) +FLOAT_MINMAX(min_d, 64, minnum) +FLOAT_MINMAX(mina_s, 32, minnummag) +FLOAT_MINMAX(mina_d, 64, minnummag) +#undef FLOAT_MINMAX + +/* ternary operations */ +#define UNFUSED_FMA(prefix, a, b, c, flags)                          \ +{                                                                    \ +    a = prefix##_mul(a, b, &env->active_fpu.fp_status);              \ +    if ((flags) & float_muladd_negate_c) {                           \ +        a = prefix##_sub(a, c, &env->active_fpu.fp_status);          \ +    } else {                                                         \ +        a = prefix##_add(a, c, &env->active_fpu.fp_status);          \ +    }                                                                \ +    if ((flags) & float_muladd_negate_result) {                      \ +        a = prefix##_chs(a);                                         \ +    }                                                                \ +} + +/* FMA based operations */ +#define FLOAT_FMA(name, type)                                        \ +uint64_t helper_float_ ## name ## _d(CPUMIPSState *env,              \ +                                     uint64_t fdt0, uint64_t fdt1,   \ +                                     uint64_t fdt2)                  \ +{                                                                    \ +    UNFUSED_FMA(float64, fdt0, fdt1, fdt2, type);                    \ +    update_fcr31(env, GETPC());                                      \ +    return fdt0;                                                     \ +}                                                                    \ +                                                                     \ +uint32_t helper_float_ ## name ## _s(CPUMIPSState *env,              \ +                                     uint32_t fst0, uint32_t fst1,   \ +                                     uint32_t fst2)                  \ +{                                                                    \ +    UNFUSED_FMA(float32, fst0, fst1, fst2, type);                    \ +    update_fcr31(env, GETPC());                                      \ +    return fst0;                                                     \ +}                                                                    \ +                                                                     \ +uint64_t helper_float_ ## name ## _ps(CPUMIPSState *env,             \ +                                      uint64_t fdt0, uint64_t fdt1,  \ +                                      uint64_t fdt2)                 \ +{                                                                    \ +    uint32_t fst0 = fdt0 & 0XFFFFFFFF;                               \ +    uint32_t fsth0 = fdt0 >> 32;                                     \ +    uint32_t fst1 = fdt1 & 0XFFFFFFFF;                               \ +    uint32_t fsth1 = fdt1 >> 32;                                     \ +    uint32_t fst2 = fdt2 & 0XFFFFFFFF;                               \ +    uint32_t fsth2 = fdt2 >> 32;                                     \ +                                                                     \ +    UNFUSED_FMA(float32, fst0, fst1, fst2, type);                    \ +    UNFUSED_FMA(float32, fsth0, fsth1, fsth2, type);                 \ +    update_fcr31(env, GETPC());                                      \ +    return ((uint64_t)fsth0 << 32) | fst0;                           \ +} +FLOAT_FMA(madd, 0) +FLOAT_FMA(msub, float_muladd_negate_c) +FLOAT_FMA(nmadd, float_muladd_negate_result) +FLOAT_FMA(nmsub, float_muladd_negate_result | float_muladd_negate_c) +#undef FLOAT_FMA + +#define FLOAT_FMADDSUB(name, bits, muladd_arg)                          \ +uint ## bits ## _t helper_float_ ## name (CPUMIPSState *env,            \ +                                          uint ## bits ## _t fs,        \ +                                          uint ## bits ## _t ft,        \ +                                          uint ## bits ## _t fd)        \ +{                                                                       \ +    uint ## bits ## _t fdret;                                           \ +                                                                        \ +    fdret = float ## bits ## _muladd(fs, ft, fd, muladd_arg,            \ +                                     &env->active_fpu.fp_status);       \ +    update_fcr31(env, GETPC());                                         \ +    return fdret;                                                       \ +} + +FLOAT_FMADDSUB(maddf_s, 32, 0) +FLOAT_FMADDSUB(maddf_d, 64, 0) +FLOAT_FMADDSUB(msubf_s, 32, float_muladd_negate_product) +FLOAT_FMADDSUB(msubf_d, 64, float_muladd_negate_product) +#undef FLOAT_FMADDSUB + +/* compare operations */ +#define FOP_COND_D(op, cond)                                   \ +void helper_cmp_d_ ## op(CPUMIPSState *env, uint64_t fdt0,     \ +                         uint64_t fdt1, int cc)                \ +{                                                              \ +    int c;                                                     \ +    c = cond;                                                  \ +    update_fcr31(env, GETPC());                                \ +    if (c)                                                     \ +        SET_FP_COND(cc, env->active_fpu);                      \ +    else                                                       \ +        CLEAR_FP_COND(cc, env->active_fpu);                    \ +}                                                              \ +void helper_cmpabs_d_ ## op(CPUMIPSState *env, uint64_t fdt0,  \ +                            uint64_t fdt1, int cc)             \ +{                                                              \ +    int c;                                                     \ +    fdt0 = float64_abs(fdt0);                                  \ +    fdt1 = float64_abs(fdt1);                                  \ +    c = cond;                                                  \ +    update_fcr31(env, GETPC());                                \ +    if (c)                                                     \ +        SET_FP_COND(cc, env->active_fpu);                      \ +    else                                                       \ +        CLEAR_FP_COND(cc, env->active_fpu);                    \ +} + +/* NOTE: the comma operator will make "cond" to eval to false, + * but float64_unordered_quiet() is still called. */ +FOP_COND_D(f,   (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status), 0)) +FOP_COND_D(un,  float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status)) +FOP_COND_D(eq,  float64_eq_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ueq, float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status)  || float64_eq_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(olt, float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ult, float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status)  || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ole, float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ule, float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status)  || float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status)) +/* NOTE: the comma operator will make "cond" to eval to false, + * but float64_unordered() is still called. */ +FOP_COND_D(sf,  (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status), 0)) +FOP_COND_D(ngle,float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status)) +FOP_COND_D(seq, float64_eq(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ngl, float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status)  || float64_eq(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(lt,  float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(nge, float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status)  || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(le,  float64_le(fdt0, fdt1, &env->active_fpu.fp_status)) +FOP_COND_D(ngt, float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status)  || float64_le(fdt0, fdt1, &env->active_fpu.fp_status)) + +#define FOP_COND_S(op, cond)                                   \ +void helper_cmp_s_ ## op(CPUMIPSState *env, uint32_t fst0,     \ +                         uint32_t fst1, int cc)                \ +{                                                              \ +    int c;                                                     \ +    c = cond;                                                  \ +    update_fcr31(env, GETPC());                                \ +    if (c)                                                     \ +        SET_FP_COND(cc, env->active_fpu);                      \ +    else                                                       \ +        CLEAR_FP_COND(cc, env->active_fpu);                    \ +}                                                              \ +void helper_cmpabs_s_ ## op(CPUMIPSState *env, uint32_t fst0,  \ +                            uint32_t fst1, int cc)             \ +{                                                              \ +    int c;                                                     \ +    fst0 = float32_abs(fst0);                                  \ +    fst1 = float32_abs(fst1);                                  \ +    c = cond;                                                  \ +    update_fcr31(env, GETPC());                                \ +    if (c)                                                     \ +        SET_FP_COND(cc, env->active_fpu);                      \ +    else                                                       \ +        CLEAR_FP_COND(cc, env->active_fpu);                    \ +} + +/* NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered_quiet() is still called. */ +FOP_COND_S(f,   (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status), 0)) +FOP_COND_S(un,  float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)) +FOP_COND_S(eq,  float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ueq, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)  || float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(olt, float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ult, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)  || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ole, float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ule, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)  || float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status)) +/* NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered() is still called. */ +FOP_COND_S(sf,  (float32_unordered(fst1, fst0, &env->active_fpu.fp_status), 0)) +FOP_COND_S(ngle,float32_unordered(fst1, fst0, &env->active_fpu.fp_status)) +FOP_COND_S(seq, float32_eq(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ngl, float32_unordered(fst1, fst0, &env->active_fpu.fp_status)  || float32_eq(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(lt,  float32_lt(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(nge, float32_unordered(fst1, fst0, &env->active_fpu.fp_status)  || float32_lt(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(le,  float32_le(fst0, fst1, &env->active_fpu.fp_status)) +FOP_COND_S(ngt, float32_unordered(fst1, fst0, &env->active_fpu.fp_status)  || float32_le(fst0, fst1, &env->active_fpu.fp_status)) + +#define FOP_COND_PS(op, condl, condh)                           \ +void helper_cmp_ps_ ## op(CPUMIPSState *env, uint64_t fdt0,     \ +                          uint64_t fdt1, int cc)                \ +{                                                               \ +    uint32_t fst0, fsth0, fst1, fsth1;                          \ +    int ch, cl;                                                 \ +    fst0 = fdt0 & 0XFFFFFFFF;                                   \ +    fsth0 = fdt0 >> 32;                                         \ +    fst1 = fdt1 & 0XFFFFFFFF;                                   \ +    fsth1 = fdt1 >> 32;                                         \ +    cl = condl;                                                 \ +    ch = condh;                                                 \ +    update_fcr31(env, GETPC());                                 \ +    if (cl)                                                     \ +        SET_FP_COND(cc, env->active_fpu);                       \ +    else                                                        \ +        CLEAR_FP_COND(cc, env->active_fpu);                     \ +    if (ch)                                                     \ +        SET_FP_COND(cc + 1, env->active_fpu);                   \ +    else                                                        \ +        CLEAR_FP_COND(cc + 1, env->active_fpu);                 \ +}                                                               \ +void helper_cmpabs_ps_ ## op(CPUMIPSState *env, uint64_t fdt0,  \ +                             uint64_t fdt1, int cc)             \ +{                                                               \ +    uint32_t fst0, fsth0, fst1, fsth1;                          \ +    int ch, cl;                                                 \ +    fst0 = float32_abs(fdt0 & 0XFFFFFFFF);                      \ +    fsth0 = float32_abs(fdt0 >> 32);                            \ +    fst1 = float32_abs(fdt1 & 0XFFFFFFFF);                      \ +    fsth1 = float32_abs(fdt1 >> 32);                            \ +    cl = condl;                                                 \ +    ch = condh;                                                 \ +    update_fcr31(env, GETPC());                                 \ +    if (cl)                                                     \ +        SET_FP_COND(cc, env->active_fpu);                       \ +    else                                                        \ +        CLEAR_FP_COND(cc, env->active_fpu);                     \ +    if (ch)                                                     \ +        SET_FP_COND(cc + 1, env->active_fpu);                   \ +    else                                                        \ +        CLEAR_FP_COND(cc + 1, env->active_fpu);                 \ +} + +/* NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered_quiet() is still called. */ +FOP_COND_PS(f,   (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status), 0), +                 (float32_unordered_quiet(fsth1, fsth0, &env->active_fpu.fp_status), 0)) +FOP_COND_PS(un,  float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status), +                 float32_unordered_quiet(fsth1, fsth0, &env->active_fpu.fp_status)) +FOP_COND_PS(eq,  float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status), +                 float32_eq_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ueq, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)    || float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status), +                 float32_unordered_quiet(fsth1, fsth0, &env->active_fpu.fp_status)  || float32_eq_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(olt, float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status), +                 float32_lt_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ult, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)    || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status), +                 float32_unordered_quiet(fsth1, fsth0, &env->active_fpu.fp_status)  || float32_lt_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ole, float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status), +                 float32_le_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ule, float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status)    || float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status), +                 float32_unordered_quiet(fsth1, fsth0, &env->active_fpu.fp_status)  || float32_le_quiet(fsth0, fsth1, &env->active_fpu.fp_status)) +/* NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered() is still called. */ +FOP_COND_PS(sf,  (float32_unordered(fst1, fst0, &env->active_fpu.fp_status), 0), +                 (float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status), 0)) +FOP_COND_PS(ngle,float32_unordered(fst1, fst0, &env->active_fpu.fp_status), +                 float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status)) +FOP_COND_PS(seq, float32_eq(fst0, fst1, &env->active_fpu.fp_status), +                 float32_eq(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ngl, float32_unordered(fst1, fst0, &env->active_fpu.fp_status)    || float32_eq(fst0, fst1, &env->active_fpu.fp_status), +                 float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status)  || float32_eq(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(lt,  float32_lt(fst0, fst1, &env->active_fpu.fp_status), +                 float32_lt(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(nge, float32_unordered(fst1, fst0, &env->active_fpu.fp_status)    || float32_lt(fst0, fst1, &env->active_fpu.fp_status), +                 float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status)  || float32_lt(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(le,  float32_le(fst0, fst1, &env->active_fpu.fp_status), +                 float32_le(fsth0, fsth1, &env->active_fpu.fp_status)) +FOP_COND_PS(ngt, float32_unordered(fst1, fst0, &env->active_fpu.fp_status)    || float32_le(fst0, fst1, &env->active_fpu.fp_status), +                 float32_unordered(fsth1, fsth0, &env->active_fpu.fp_status)  || float32_le(fsth0, fsth1, &env->active_fpu.fp_status)) + +/* R6 compare operations */ +#define FOP_CONDN_D(op, cond)                                       \ +uint64_t helper_r6_cmp_d_ ## op(CPUMIPSState * env, uint64_t fdt0,  \ +                         uint64_t fdt1)                             \ +{                                                                   \ +    uint64_t c;                                                     \ +    c = cond;                                                       \ +    update_fcr31(env, GETPC());                                     \ +    if (c) {                                                        \ +        return -1;                                                  \ +    } else {                                                        \ +        return 0;                                                   \ +    }                                                               \ +} + +/* NOTE: the comma operator will make "cond" to eval to false, + * but float64_unordered_quiet() is still called. */ +FOP_CONDN_D(af,  (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status), 0)) +FOP_CONDN_D(un,  (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status))) +FOP_CONDN_D(eq,  (float64_eq_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(ueq, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) +                  || float64_eq_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(lt,  (float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(ult, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) +                  || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(le,  (float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(ule, (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) +                  || float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +/* NOTE: the comma operator will make "cond" to eval to false, + * but float64_unordered() is still called. */ +FOP_CONDN_D(saf,  (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status), 0)) +FOP_CONDN_D(sun,  (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status))) +FOP_CONDN_D(seq,  (float64_eq(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sueq, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_eq(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(slt,  (float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sult, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sle,  (float64_le(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sule, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_le(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(or,   (float64_le_quiet(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_le_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(une,  (float64_unordered_quiet(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_lt_quiet(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(ne,   (float64_lt_quiet(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_lt_quiet(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sor,  (float64_le(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_le(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sune, (float64_unordered(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_lt(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))) +FOP_CONDN_D(sne,  (float64_lt(fdt1, fdt0, &env->active_fpu.fp_status) +                   || float64_lt(fdt0, fdt1, &env->active_fpu.fp_status))) + +#define FOP_CONDN_S(op, cond)                                       \ +uint32_t helper_r6_cmp_s_ ## op(CPUMIPSState * env, uint32_t fst0,  \ +                         uint32_t fst1)                             \ +{                                                                   \ +    uint64_t c;                                                     \ +    c = cond;                                                       \ +    update_fcr31(env, GETPC());                                     \ +    if (c) {                                                        \ +        return -1;                                                  \ +    } else {                                                        \ +        return 0;                                                   \ +    }                                                               \ +} + +/* NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered_quiet() is still called. */ +FOP_CONDN_S(af,   (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status), 0)) +FOP_CONDN_S(un,   (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status))) +FOP_CONDN_S(eq,   (float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(ueq,  (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_eq_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(lt,   (float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(ult,  (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(le,   (float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(ule,  (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status))) +/* NOTE: the comma operator will make "cond" to eval to false, + * but float32_unordered() is still called. */ +FOP_CONDN_S(saf,  (float32_unordered(fst1, fst0, &env->active_fpu.fp_status), 0)) +FOP_CONDN_S(sun,  (float32_unordered(fst1, fst0, &env->active_fpu.fp_status))) +FOP_CONDN_S(seq,  (float32_eq(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sueq, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_eq(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(slt,  (float32_lt(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sult, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_lt(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sle,  (float32_le(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sule, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_le(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(or,   (float32_le_quiet(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_le_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(une,  (float32_unordered_quiet(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_lt_quiet(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(ne,   (float32_lt_quiet(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_lt_quiet(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sor,  (float32_le(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_le(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sune, (float32_unordered(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_lt(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_lt(fst0, fst1, &env->active_fpu.fp_status))) +FOP_CONDN_S(sne,  (float32_lt(fst1, fst0, &env->active_fpu.fp_status) +                   || float32_lt(fst0, fst1, &env->active_fpu.fp_status))) + +/* MSA */ +/* Data format min and max values */ +#define DF_BITS(df) (1 << ((df) + 3)) + +/* Element-by-element access macros */ +#define DF_ELEMENTS(df) (MSA_WRLEN / DF_BITS(df)) + +#if !defined(CONFIG_USER_ONLY) +#define MEMOP_IDX(DF)                                           \ +        TCGMemOpIdx oi = make_memop_idx(MO_TE | DF | MO_UNALN,  \ +                                        cpu_mmu_index(env)); +#else +#define MEMOP_IDX(DF) +#endif + +#define MSA_LD_DF(DF, TYPE, LD_INSN, ...)                               \ +void helper_msa_ld_ ## TYPE(CPUMIPSState *env, uint32_t wd,             \ +                            target_ulong addr)                          \ +{                                                                       \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                          \ +    wr_t wx;                                                            \ +    int i;                                                              \ +    MEMOP_IDX(DF)                                                       \ +    for (i = 0; i < DF_ELEMENTS(DF); i++) {                             \ +        wx.TYPE[i] = LD_INSN(env, addr + (i << DF), ##__VA_ARGS__);     \ +    }                                                                   \ +    memcpy(pwd, &wx, sizeof(wr_t));                                     \ +} + +#if !defined(CONFIG_USER_ONLY) +MSA_LD_DF(DF_BYTE,   b, helper_ret_ldub_mmu, oi, GETRA()) +MSA_LD_DF(DF_HALF,   h, helper_ret_lduw_mmu, oi, GETRA()) +MSA_LD_DF(DF_WORD,   w, helper_ret_ldul_mmu, oi, GETRA()) +MSA_LD_DF(DF_DOUBLE, d, helper_ret_ldq_mmu,  oi, GETRA()) +#else +MSA_LD_DF(DF_BYTE,   b, cpu_ldub_data) +MSA_LD_DF(DF_HALF,   h, cpu_lduw_data) +MSA_LD_DF(DF_WORD,   w, cpu_ldl_data) +MSA_LD_DF(DF_DOUBLE, d, cpu_ldq_data) +#endif + +#define MSA_PAGESPAN(x) \ +        ((((x) & ~TARGET_PAGE_MASK) + MSA_WRLEN/8 - 1) >= TARGET_PAGE_SIZE) + +static inline void ensure_writable_pages(CPUMIPSState *env, +                                         target_ulong addr, +                                         int mmu_idx, +                                         uintptr_t retaddr) +{ +#if !defined(CONFIG_USER_ONLY) +    target_ulong page_addr; +    if (unlikely(MSA_PAGESPAN(addr))) { +        /* first page */ +        probe_write(env, addr, mmu_idx, retaddr); +        /* second page */ +        page_addr = (addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; +        probe_write(env, page_addr, mmu_idx, retaddr); +    } +#endif +} + +#define MSA_ST_DF(DF, TYPE, ST_INSN, ...)                               \ +void helper_msa_st_ ## TYPE(CPUMIPSState *env, uint32_t wd,             \ +                            target_ulong addr)                          \ +{                                                                       \ +    wr_t *pwd = &(env->active_fpu.fpr[wd].wr);                          \ +    int mmu_idx = cpu_mmu_index(env);                                   \ +    int i;                                                              \ +    MEMOP_IDX(DF)                                                       \ +    ensure_writable_pages(env, addr, mmu_idx, GETRA());                 \ +    for (i = 0; i < DF_ELEMENTS(DF); i++) {                             \ +        ST_INSN(env, addr + (i << DF), pwd->TYPE[i], ##__VA_ARGS__);    \ +    }                                                                   \ +} + +#if !defined(CONFIG_USER_ONLY) +MSA_ST_DF(DF_BYTE,   b, helper_ret_stb_mmu, oi, GETRA()) +MSA_ST_DF(DF_HALF,   h, helper_ret_stw_mmu, oi, GETRA()) +MSA_ST_DF(DF_WORD,   w, helper_ret_stl_mmu, oi, GETRA()) +MSA_ST_DF(DF_DOUBLE, d, helper_ret_stq_mmu, oi, GETRA()) +#else +MSA_ST_DF(DF_BYTE,   b, cpu_stb_data) +MSA_ST_DF(DF_HALF,   h, cpu_stw_data) +MSA_ST_DF(DF_WORD,   w, cpu_stl_data) +MSA_ST_DF(DF_DOUBLE, d, cpu_stq_data) +#endif diff --git a/target-mips/translate.c b/target-mips/translate.c new file mode 100644 index 00000000..22ef84df --- /dev/null +++ b/target-mips/translate.c @@ -0,0 +1,20731 @@ +/* + *  MIPS32 emulation for qemu: main translation routines. + * + *  Copyright (c) 2004-2005 Jocelyn Mayer + *  Copyright (c) 2006 Marius Groeger (FPU operations) + *  Copyright (c) 2006 Thiemo Seufer (MIPS32R2 support) + *  Copyright (c) 2009 CodeSourcery (MIPS16 and microMIPS support) + *  Copyright (c) 2012 Jia Liu & Dongxue Zhang (MIPS ASE DSP support) + * + * 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 "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 "sysemu/kvm.h" +#include "exec/semihost.h" + +#include "trace-tcg.h" + + +#define MIPS_DEBUG_DISAS 0 +//#define MIPS_DEBUG_SIGN_EXTENSIONS + +/* MIPS major opcodes */ +#define MASK_OP_MAJOR(op)  (op & (0x3F << 26)) + +enum { +    /* indirect opcode tables */ +    OPC_SPECIAL  = (0x00 << 26), +    OPC_REGIMM   = (0x01 << 26), +    OPC_CP0      = (0x10 << 26), +    OPC_CP1      = (0x11 << 26), +    OPC_CP2      = (0x12 << 26), +    OPC_CP3      = (0x13 << 26), +    OPC_SPECIAL2 = (0x1C << 26), +    OPC_SPECIAL3 = (0x1F << 26), +    /* arithmetic with immediate */ +    OPC_ADDI     = (0x08 << 26), +    OPC_ADDIU    = (0x09 << 26), +    OPC_SLTI     = (0x0A << 26), +    OPC_SLTIU    = (0x0B << 26), +    /* logic with immediate */ +    OPC_ANDI     = (0x0C << 26), +    OPC_ORI      = (0x0D << 26), +    OPC_XORI     = (0x0E << 26), +    OPC_LUI      = (0x0F << 26), +    /* arithmetic with immediate */ +    OPC_DADDI    = (0x18 << 26), +    OPC_DADDIU   = (0x19 << 26), +    /* Jump and branches */ +    OPC_J        = (0x02 << 26), +    OPC_JAL      = (0x03 << 26), +    OPC_BEQ      = (0x04 << 26),  /* Unconditional if rs = rt = 0 (B) */ +    OPC_BEQL     = (0x14 << 26), +    OPC_BNE      = (0x05 << 26), +    OPC_BNEL     = (0x15 << 26), +    OPC_BLEZ     = (0x06 << 26), +    OPC_BLEZL    = (0x16 << 26), +    OPC_BGTZ     = (0x07 << 26), +    OPC_BGTZL    = (0x17 << 26), +    OPC_JALX     = (0x1D << 26), +    OPC_DAUI     = (0x1D << 26), +    /* Load and stores */ +    OPC_LDL      = (0x1A << 26), +    OPC_LDR      = (0x1B << 26), +    OPC_LB       = (0x20 << 26), +    OPC_LH       = (0x21 << 26), +    OPC_LWL      = (0x22 << 26), +    OPC_LW       = (0x23 << 26), +    OPC_LWPC     = OPC_LW | 0x5, +    OPC_LBU      = (0x24 << 26), +    OPC_LHU      = (0x25 << 26), +    OPC_LWR      = (0x26 << 26), +    OPC_LWU      = (0x27 << 26), +    OPC_SB       = (0x28 << 26), +    OPC_SH       = (0x29 << 26), +    OPC_SWL      = (0x2A << 26), +    OPC_SW       = (0x2B << 26), +    OPC_SDL      = (0x2C << 26), +    OPC_SDR      = (0x2D << 26), +    OPC_SWR      = (0x2E << 26), +    OPC_LL       = (0x30 << 26), +    OPC_LLD      = (0x34 << 26), +    OPC_LD       = (0x37 << 26), +    OPC_LDPC     = OPC_LD | 0x5, +    OPC_SC       = (0x38 << 26), +    OPC_SCD      = (0x3C << 26), +    OPC_SD       = (0x3F << 26), +    /* Floating point load/store */ +    OPC_LWC1     = (0x31 << 26), +    OPC_LWC2     = (0x32 << 26), +    OPC_LDC1     = (0x35 << 26), +    OPC_LDC2     = (0x36 << 26), +    OPC_SWC1     = (0x39 << 26), +    OPC_SWC2     = (0x3A << 26), +    OPC_SDC1     = (0x3D << 26), +    OPC_SDC2     = (0x3E << 26), +    /* Compact Branches */ +    OPC_BLEZALC  = (0x06 << 26), +    OPC_BGEZALC  = (0x06 << 26), +    OPC_BGEUC    = (0x06 << 26), +    OPC_BGTZALC  = (0x07 << 26), +    OPC_BLTZALC  = (0x07 << 26), +    OPC_BLTUC    = (0x07 << 26), +    OPC_BOVC     = (0x08 << 26), +    OPC_BEQZALC  = (0x08 << 26), +    OPC_BEQC     = (0x08 << 26), +    OPC_BLEZC    = (0x16 << 26), +    OPC_BGEZC    = (0x16 << 26), +    OPC_BGEC     = (0x16 << 26), +    OPC_BGTZC    = (0x17 << 26), +    OPC_BLTZC    = (0x17 << 26), +    OPC_BLTC     = (0x17 << 26), +    OPC_BNVC     = (0x18 << 26), +    OPC_BNEZALC  = (0x18 << 26), +    OPC_BNEC     = (0x18 << 26), +    OPC_BC       = (0x32 << 26), +    OPC_BEQZC    = (0x36 << 26), +    OPC_JIC      = (0x36 << 26), +    OPC_BALC     = (0x3A << 26), +    OPC_BNEZC    = (0x3E << 26), +    OPC_JIALC    = (0x3E << 26), +    /* MDMX ASE specific */ +    OPC_MDMX     = (0x1E << 26), +    /* MSA ASE, same as MDMX */ +    OPC_MSA      = OPC_MDMX, +    /* Cache and prefetch */ +    OPC_CACHE    = (0x2F << 26), +    OPC_PREF     = (0x33 << 26), +    /* PC-relative address computation / loads */ +    OPC_PCREL    = (0x3B << 26), +}; + +/* PC-relative address computation / loads  */ +#define MASK_OPC_PCREL_TOP2BITS(op)  (MASK_OP_MAJOR(op) | (op & (3 << 19))) +#define MASK_OPC_PCREL_TOP5BITS(op)  (MASK_OP_MAJOR(op) | (op & (0x1f << 16))) +enum { +    /* Instructions determined by bits 19 and 20 */ +    OPC_ADDIUPC = OPC_PCREL | (0 << 19), +    R6_OPC_LWPC = OPC_PCREL | (1 << 19), +    OPC_LWUPC   = OPC_PCREL | (2 << 19), + +    /* Instructions determined by bits 16 ... 20 */ +    OPC_AUIPC   = OPC_PCREL | (0x1e << 16), +    OPC_ALUIPC  = OPC_PCREL | (0x1f << 16), + +    /* Other */ +    R6_OPC_LDPC = OPC_PCREL | (6 << 18), +}; + +/* MIPS special opcodes */ +#define MASK_SPECIAL(op)   MASK_OP_MAJOR(op) | (op & 0x3F) + +enum { +    /* Shifts */ +    OPC_SLL      = 0x00 | OPC_SPECIAL, +    /* NOP is SLL r0, r0, 0   */ +    /* SSNOP is SLL r0, r0, 1 */ +    /* EHB is SLL r0, r0, 3 */ +    OPC_SRL      = 0x02 | OPC_SPECIAL, /* also ROTR */ +    OPC_ROTR     = OPC_SRL | (1 << 21), +    OPC_SRA      = 0x03 | OPC_SPECIAL, +    OPC_SLLV     = 0x04 | OPC_SPECIAL, +    OPC_SRLV     = 0x06 | OPC_SPECIAL, /* also ROTRV */ +    OPC_ROTRV    = OPC_SRLV | (1 << 6), +    OPC_SRAV     = 0x07 | OPC_SPECIAL, +    OPC_DSLLV    = 0x14 | OPC_SPECIAL, +    OPC_DSRLV    = 0x16 | OPC_SPECIAL, /* also DROTRV */ +    OPC_DROTRV   = OPC_DSRLV | (1 << 6), +    OPC_DSRAV    = 0x17 | OPC_SPECIAL, +    OPC_DSLL     = 0x38 | OPC_SPECIAL, +    OPC_DSRL     = 0x3A | OPC_SPECIAL, /* also DROTR */ +    OPC_DROTR    = OPC_DSRL | (1 << 21), +    OPC_DSRA     = 0x3B | OPC_SPECIAL, +    OPC_DSLL32   = 0x3C | OPC_SPECIAL, +    OPC_DSRL32   = 0x3E | OPC_SPECIAL, /* also DROTR32 */ +    OPC_DROTR32  = OPC_DSRL32 | (1 << 21), +    OPC_DSRA32   = 0x3F | OPC_SPECIAL, +    /* Multiplication / division */ +    OPC_MULT     = 0x18 | OPC_SPECIAL, +    OPC_MULTU    = 0x19 | OPC_SPECIAL, +    OPC_DIV      = 0x1A | OPC_SPECIAL, +    OPC_DIVU     = 0x1B | OPC_SPECIAL, +    OPC_DMULT    = 0x1C | OPC_SPECIAL, +    OPC_DMULTU   = 0x1D | OPC_SPECIAL, +    OPC_DDIV     = 0x1E | OPC_SPECIAL, +    OPC_DDIVU    = 0x1F | OPC_SPECIAL, + +    /* 2 registers arithmetic / logic */ +    OPC_ADD      = 0x20 | OPC_SPECIAL, +    OPC_ADDU     = 0x21 | OPC_SPECIAL, +    OPC_SUB      = 0x22 | OPC_SPECIAL, +    OPC_SUBU     = 0x23 | OPC_SPECIAL, +    OPC_AND      = 0x24 | OPC_SPECIAL, +    OPC_OR       = 0x25 | OPC_SPECIAL, +    OPC_XOR      = 0x26 | OPC_SPECIAL, +    OPC_NOR      = 0x27 | OPC_SPECIAL, +    OPC_SLT      = 0x2A | OPC_SPECIAL, +    OPC_SLTU     = 0x2B | OPC_SPECIAL, +    OPC_DADD     = 0x2C | OPC_SPECIAL, +    OPC_DADDU    = 0x2D | OPC_SPECIAL, +    OPC_DSUB     = 0x2E | OPC_SPECIAL, +    OPC_DSUBU    = 0x2F | OPC_SPECIAL, +    /* Jumps */ +    OPC_JR       = 0x08 | OPC_SPECIAL, /* Also JR.HB */ +    OPC_JALR     = 0x09 | OPC_SPECIAL, /* Also JALR.HB */ +    /* Traps */ +    OPC_TGE      = 0x30 | OPC_SPECIAL, +    OPC_TGEU     = 0x31 | OPC_SPECIAL, +    OPC_TLT      = 0x32 | OPC_SPECIAL, +    OPC_TLTU     = 0x33 | OPC_SPECIAL, +    OPC_TEQ      = 0x34 | OPC_SPECIAL, +    OPC_TNE      = 0x36 | OPC_SPECIAL, +    /* HI / LO registers load & stores */ +    OPC_MFHI     = 0x10 | OPC_SPECIAL, +    OPC_MTHI     = 0x11 | OPC_SPECIAL, +    OPC_MFLO     = 0x12 | OPC_SPECIAL, +    OPC_MTLO     = 0x13 | OPC_SPECIAL, +    /* Conditional moves */ +    OPC_MOVZ     = 0x0A | OPC_SPECIAL, +    OPC_MOVN     = 0x0B | OPC_SPECIAL, + +    OPC_SELEQZ   = 0x35 | OPC_SPECIAL, +    OPC_SELNEZ   = 0x37 | OPC_SPECIAL, + +    OPC_MOVCI    = 0x01 | OPC_SPECIAL, + +    /* Special */ +    OPC_PMON     = 0x05 | OPC_SPECIAL, /* unofficial */ +    OPC_SYSCALL  = 0x0C | OPC_SPECIAL, +    OPC_BREAK    = 0x0D | OPC_SPECIAL, +    OPC_SPIM     = 0x0E | OPC_SPECIAL, /* unofficial */ +    OPC_SYNC     = 0x0F | OPC_SPECIAL, + +    OPC_SPECIAL28_RESERVED = 0x28 | OPC_SPECIAL, +    OPC_SPECIAL29_RESERVED = 0x29 | OPC_SPECIAL, +    OPC_SPECIAL39_RESERVED = 0x39 | OPC_SPECIAL, +    OPC_SPECIAL3D_RESERVED = 0x3D | OPC_SPECIAL, +}; + +/* R6 Multiply and Divide instructions have the same Opcode +   and function field as legacy OPC_MULT[U]/OPC_DIV[U] */ +#define MASK_R6_MULDIV(op)   (MASK_SPECIAL(op) | (op & (0x7ff))) + +enum { +    R6_OPC_MUL   = OPC_MULT  | (2 << 6), +    R6_OPC_MUH   = OPC_MULT  | (3 << 6), +    R6_OPC_MULU  = OPC_MULTU | (2 << 6), +    R6_OPC_MUHU  = OPC_MULTU | (3 << 6), +    R6_OPC_DIV   = OPC_DIV   | (2 << 6), +    R6_OPC_MOD   = OPC_DIV   | (3 << 6), +    R6_OPC_DIVU  = OPC_DIVU  | (2 << 6), +    R6_OPC_MODU  = OPC_DIVU  | (3 << 6), + +    R6_OPC_DMUL   = OPC_DMULT  | (2 << 6), +    R6_OPC_DMUH   = OPC_DMULT  | (3 << 6), +    R6_OPC_DMULU  = OPC_DMULTU | (2 << 6), +    R6_OPC_DMUHU  = OPC_DMULTU | (3 << 6), +    R6_OPC_DDIV   = OPC_DDIV   | (2 << 6), +    R6_OPC_DMOD   = OPC_DDIV   | (3 << 6), +    R6_OPC_DDIVU  = OPC_DDIVU  | (2 << 6), +    R6_OPC_DMODU  = OPC_DDIVU  | (3 << 6), + +    R6_OPC_CLZ      = 0x10 | OPC_SPECIAL, +    R6_OPC_CLO      = 0x11 | OPC_SPECIAL, +    R6_OPC_DCLZ     = 0x12 | OPC_SPECIAL, +    R6_OPC_DCLO     = 0x13 | OPC_SPECIAL, +    R6_OPC_SDBBP    = 0x0e | OPC_SPECIAL, + +    OPC_LSA  = 0x05 | OPC_SPECIAL, +    OPC_DLSA = 0x15 | OPC_SPECIAL, +}; + +/* Multiplication variants of the vr54xx. */ +#define MASK_MUL_VR54XX(op)   MASK_SPECIAL(op) | (op & (0x1F << 6)) + +enum { +    OPC_VR54XX_MULS    = (0x03 << 6) | OPC_MULT, +    OPC_VR54XX_MULSU   = (0x03 << 6) | OPC_MULTU, +    OPC_VR54XX_MACC    = (0x05 << 6) | OPC_MULT, +    OPC_VR54XX_MACCU   = (0x05 << 6) | OPC_MULTU, +    OPC_VR54XX_MSAC    = (0x07 << 6) | OPC_MULT, +    OPC_VR54XX_MSACU   = (0x07 << 6) | OPC_MULTU, +    OPC_VR54XX_MULHI   = (0x09 << 6) | OPC_MULT, +    OPC_VR54XX_MULHIU  = (0x09 << 6) | OPC_MULTU, +    OPC_VR54XX_MULSHI  = (0x0B << 6) | OPC_MULT, +    OPC_VR54XX_MULSHIU = (0x0B << 6) | OPC_MULTU, +    OPC_VR54XX_MACCHI  = (0x0D << 6) | OPC_MULT, +    OPC_VR54XX_MACCHIU = (0x0D << 6) | OPC_MULTU, +    OPC_VR54XX_MSACHI  = (0x0F << 6) | OPC_MULT, +    OPC_VR54XX_MSACHIU = (0x0F << 6) | OPC_MULTU, +}; + +/* REGIMM (rt field) opcodes */ +#define MASK_REGIMM(op)    MASK_OP_MAJOR(op) | (op & (0x1F << 16)) + +enum { +    OPC_BLTZ     = (0x00 << 16) | OPC_REGIMM, +    OPC_BLTZL    = (0x02 << 16) | OPC_REGIMM, +    OPC_BGEZ     = (0x01 << 16) | OPC_REGIMM, +    OPC_BGEZL    = (0x03 << 16) | OPC_REGIMM, +    OPC_BLTZAL   = (0x10 << 16) | OPC_REGIMM, +    OPC_BLTZALL  = (0x12 << 16) | OPC_REGIMM, +    OPC_BGEZAL   = (0x11 << 16) | OPC_REGIMM, +    OPC_BGEZALL  = (0x13 << 16) | OPC_REGIMM, +    OPC_TGEI     = (0x08 << 16) | OPC_REGIMM, +    OPC_TGEIU    = (0x09 << 16) | OPC_REGIMM, +    OPC_TLTI     = (0x0A << 16) | OPC_REGIMM, +    OPC_TLTIU    = (0x0B << 16) | OPC_REGIMM, +    OPC_TEQI     = (0x0C << 16) | OPC_REGIMM, +    OPC_TNEI     = (0x0E << 16) | OPC_REGIMM, +    OPC_SYNCI    = (0x1F << 16) | OPC_REGIMM, + +    OPC_DAHI     = (0x06 << 16) | OPC_REGIMM, +    OPC_DATI     = (0x1e << 16) | OPC_REGIMM, +}; + +/* Special2 opcodes */ +#define MASK_SPECIAL2(op)  MASK_OP_MAJOR(op) | (op & 0x3F) + +enum { +    /* Multiply & xxx operations */ +    OPC_MADD     = 0x00 | OPC_SPECIAL2, +    OPC_MADDU    = 0x01 | OPC_SPECIAL2, +    OPC_MUL      = 0x02 | OPC_SPECIAL2, +    OPC_MSUB     = 0x04 | OPC_SPECIAL2, +    OPC_MSUBU    = 0x05 | OPC_SPECIAL2, +    /* Loongson 2F */ +    OPC_MULT_G_2F   = 0x10 | OPC_SPECIAL2, +    OPC_DMULT_G_2F  = 0x11 | OPC_SPECIAL2, +    OPC_MULTU_G_2F  = 0x12 | OPC_SPECIAL2, +    OPC_DMULTU_G_2F = 0x13 | OPC_SPECIAL2, +    OPC_DIV_G_2F    = 0x14 | OPC_SPECIAL2, +    OPC_DDIV_G_2F   = 0x15 | OPC_SPECIAL2, +    OPC_DIVU_G_2F   = 0x16 | OPC_SPECIAL2, +    OPC_DDIVU_G_2F  = 0x17 | OPC_SPECIAL2, +    OPC_MOD_G_2F    = 0x1c | OPC_SPECIAL2, +    OPC_DMOD_G_2F   = 0x1d | OPC_SPECIAL2, +    OPC_MODU_G_2F   = 0x1e | OPC_SPECIAL2, +    OPC_DMODU_G_2F  = 0x1f | OPC_SPECIAL2, +    /* Misc */ +    OPC_CLZ      = 0x20 | OPC_SPECIAL2, +    OPC_CLO      = 0x21 | OPC_SPECIAL2, +    OPC_DCLZ     = 0x24 | OPC_SPECIAL2, +    OPC_DCLO     = 0x25 | OPC_SPECIAL2, +    /* Special */ +    OPC_SDBBP    = 0x3F | OPC_SPECIAL2, +}; + +/* Special3 opcodes */ +#define MASK_SPECIAL3(op)  MASK_OP_MAJOR(op) | (op & 0x3F) + +enum { +    OPC_EXT      = 0x00 | OPC_SPECIAL3, +    OPC_DEXTM    = 0x01 | OPC_SPECIAL3, +    OPC_DEXTU    = 0x02 | OPC_SPECIAL3, +    OPC_DEXT     = 0x03 | OPC_SPECIAL3, +    OPC_INS      = 0x04 | OPC_SPECIAL3, +    OPC_DINSM    = 0x05 | OPC_SPECIAL3, +    OPC_DINSU    = 0x06 | OPC_SPECIAL3, +    OPC_DINS     = 0x07 | OPC_SPECIAL3, +    OPC_FORK     = 0x08 | OPC_SPECIAL3, +    OPC_YIELD    = 0x09 | OPC_SPECIAL3, +    OPC_BSHFL    = 0x20 | OPC_SPECIAL3, +    OPC_DBSHFL   = 0x24 | OPC_SPECIAL3, +    OPC_RDHWR    = 0x3B | OPC_SPECIAL3, + +    /* Loongson 2E */ +    OPC_MULT_G_2E   = 0x18 | OPC_SPECIAL3, +    OPC_MULTU_G_2E  = 0x19 | OPC_SPECIAL3, +    OPC_DIV_G_2E    = 0x1A | OPC_SPECIAL3, +    OPC_DIVU_G_2E   = 0x1B | OPC_SPECIAL3, +    OPC_DMULT_G_2E  = 0x1C | OPC_SPECIAL3, +    OPC_DMULTU_G_2E = 0x1D | OPC_SPECIAL3, +    OPC_DDIV_G_2E   = 0x1E | OPC_SPECIAL3, +    OPC_DDIVU_G_2E  = 0x1F | OPC_SPECIAL3, +    OPC_MOD_G_2E    = 0x22 | OPC_SPECIAL3, +    OPC_MODU_G_2E   = 0x23 | OPC_SPECIAL3, +    OPC_DMOD_G_2E   = 0x26 | OPC_SPECIAL3, +    OPC_DMODU_G_2E  = 0x27 | OPC_SPECIAL3, + +    /* MIPS DSP Load */ +    OPC_LX_DSP         = 0x0A | OPC_SPECIAL3, +    /* MIPS DSP Arithmetic */ +    OPC_ADDU_QB_DSP    = 0x10 | OPC_SPECIAL3, +    OPC_ADDU_OB_DSP    = 0x14 | OPC_SPECIAL3, +    OPC_ABSQ_S_PH_DSP  = 0x12 | OPC_SPECIAL3, +    OPC_ABSQ_S_QH_DSP  = 0x16 | OPC_SPECIAL3, +    /* OPC_ADDUH_QB_DSP is same as OPC_MULT_G_2E.  */ +    /* OPC_ADDUH_QB_DSP   = 0x18 | OPC_SPECIAL3,  */ +    OPC_CMPU_EQ_QB_DSP = 0x11 | OPC_SPECIAL3, +    OPC_CMPU_EQ_OB_DSP = 0x15 | OPC_SPECIAL3, +    /* MIPS DSP GPR-Based Shift Sub-class */ +    OPC_SHLL_QB_DSP    = 0x13 | OPC_SPECIAL3, +    OPC_SHLL_OB_DSP    = 0x17 | OPC_SPECIAL3, +    /* MIPS DSP Multiply Sub-class insns */ +    /* OPC_MUL_PH_DSP is same as OPC_ADDUH_QB_DSP.  */ +    /* OPC_MUL_PH_DSP     = 0x18 | OPC_SPECIAL3,  */ +    OPC_DPA_W_PH_DSP   = 0x30 | OPC_SPECIAL3, +    OPC_DPAQ_W_QH_DSP  = 0x34 | OPC_SPECIAL3, +    /* DSP Bit/Manipulation Sub-class */ +    OPC_INSV_DSP       = 0x0C | OPC_SPECIAL3, +    OPC_DINSV_DSP      = 0x0D | OPC_SPECIAL3, +    /* MIPS DSP Append Sub-class */ +    OPC_APPEND_DSP     = 0x31 | OPC_SPECIAL3, +    OPC_DAPPEND_DSP    = 0x35 | OPC_SPECIAL3, +    /* MIPS DSP Accumulator and DSPControl Access Sub-class */ +    OPC_EXTR_W_DSP     = 0x38 | OPC_SPECIAL3, +    OPC_DEXTR_W_DSP    = 0x3C | OPC_SPECIAL3, + +    /* R6 */ +    R6_OPC_PREF        = 0x35 | OPC_SPECIAL3, +    R6_OPC_CACHE       = 0x25 | OPC_SPECIAL3, +    R6_OPC_LL          = 0x36 | OPC_SPECIAL3, +    R6_OPC_SC          = 0x26 | OPC_SPECIAL3, +    R6_OPC_LLD         = 0x37 | OPC_SPECIAL3, +    R6_OPC_SCD         = 0x27 | OPC_SPECIAL3, +}; + +/* BSHFL opcodes */ +#define MASK_BSHFL(op)     MASK_SPECIAL3(op) | (op & (0x1F << 6)) + +enum { +    OPC_WSBH      = (0x02 << 6) | OPC_BSHFL, +    OPC_SEB       = (0x10 << 6) | OPC_BSHFL, +    OPC_SEH       = (0x18 << 6) | OPC_BSHFL, +    OPC_ALIGN     = (0x08 << 6) | OPC_BSHFL, /* 010.bp */ +    OPC_ALIGN_END = (0x0B << 6) | OPC_BSHFL, /* 010.00 to 010.11 */ +    OPC_BITSWAP   = (0x00 << 6) | OPC_BSHFL  /* 00000 */ +}; + +/* DBSHFL opcodes */ +#define MASK_DBSHFL(op)    MASK_SPECIAL3(op) | (op & (0x1F << 6)) + +enum { +    OPC_DSBH       = (0x02 << 6) | OPC_DBSHFL, +    OPC_DSHD       = (0x05 << 6) | OPC_DBSHFL, +    OPC_DALIGN     = (0x08 << 6) | OPC_DBSHFL, /* 01.bp */ +    OPC_DALIGN_END = (0x0F << 6) | OPC_DBSHFL, /* 01.000 to 01.111 */ +    OPC_DBITSWAP   = (0x00 << 6) | OPC_DBSHFL, /* 00000 */ +}; + +/* MIPS DSP REGIMM opcodes */ +enum { +    OPC_BPOSGE32 = (0x1C << 16) | OPC_REGIMM, +    OPC_BPOSGE64 = (0x1D << 16) | OPC_REGIMM, +}; + +#define MASK_LX(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +/* MIPS DSP Load */ +enum { +    OPC_LBUX = (0x06 << 6) | OPC_LX_DSP, +    OPC_LHX  = (0x04 << 6) | OPC_LX_DSP, +    OPC_LWX  = (0x00 << 6) | OPC_LX_DSP, +    OPC_LDX = (0x08 << 6) | OPC_LX_DSP, +}; + +#define MASK_ADDU_QB(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Arithmetic Sub-class */ +    OPC_ADDQ_PH        = (0x0A << 6) | OPC_ADDU_QB_DSP, +    OPC_ADDQ_S_PH      = (0x0E << 6) | OPC_ADDU_QB_DSP, +    OPC_ADDQ_S_W       = (0x16 << 6) | OPC_ADDU_QB_DSP, +    OPC_ADDU_QB        = (0x00 << 6) | OPC_ADDU_QB_DSP, +    OPC_ADDU_S_QB      = (0x04 << 6) | OPC_ADDU_QB_DSP, +    OPC_ADDU_PH        = (0x08 << 6) | OPC_ADDU_QB_DSP, +    OPC_ADDU_S_PH      = (0x0C << 6) | OPC_ADDU_QB_DSP, +    OPC_SUBQ_PH        = (0x0B << 6) | OPC_ADDU_QB_DSP, +    OPC_SUBQ_S_PH      = (0x0F << 6) | OPC_ADDU_QB_DSP, +    OPC_SUBQ_S_W       = (0x17 << 6) | OPC_ADDU_QB_DSP, +    OPC_SUBU_QB        = (0x01 << 6) | OPC_ADDU_QB_DSP, +    OPC_SUBU_S_QB      = (0x05 << 6) | OPC_ADDU_QB_DSP, +    OPC_SUBU_PH        = (0x09 << 6) | OPC_ADDU_QB_DSP, +    OPC_SUBU_S_PH      = (0x0D << 6) | OPC_ADDU_QB_DSP, +    OPC_ADDSC          = (0x10 << 6) | OPC_ADDU_QB_DSP, +    OPC_ADDWC          = (0x11 << 6) | OPC_ADDU_QB_DSP, +    OPC_MODSUB         = (0x12 << 6) | OPC_ADDU_QB_DSP, +    OPC_RADDU_W_QB     = (0x14 << 6) | OPC_ADDU_QB_DSP, +    /* MIPS DSP Multiply Sub-class insns */ +    OPC_MULEU_S_PH_QBL = (0x06 << 6) | OPC_ADDU_QB_DSP, +    OPC_MULEU_S_PH_QBR = (0x07 << 6) | OPC_ADDU_QB_DSP, +    OPC_MULQ_RS_PH     = (0x1F << 6) | OPC_ADDU_QB_DSP, +    OPC_MULEQ_S_W_PHL  = (0x1C << 6) | OPC_ADDU_QB_DSP, +    OPC_MULEQ_S_W_PHR  = (0x1D << 6) | OPC_ADDU_QB_DSP, +    OPC_MULQ_S_PH      = (0x1E << 6) | OPC_ADDU_QB_DSP, +}; + +#define OPC_ADDUH_QB_DSP OPC_MULT_G_2E +#define MASK_ADDUH_QB(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Arithmetic Sub-class */ +    OPC_ADDUH_QB   = (0x00 << 6) | OPC_ADDUH_QB_DSP, +    OPC_ADDUH_R_QB = (0x02 << 6) | OPC_ADDUH_QB_DSP, +    OPC_ADDQH_PH   = (0x08 << 6) | OPC_ADDUH_QB_DSP, +    OPC_ADDQH_R_PH = (0x0A << 6) | OPC_ADDUH_QB_DSP, +    OPC_ADDQH_W    = (0x10 << 6) | OPC_ADDUH_QB_DSP, +    OPC_ADDQH_R_W  = (0x12 << 6) | OPC_ADDUH_QB_DSP, +    OPC_SUBUH_QB   = (0x01 << 6) | OPC_ADDUH_QB_DSP, +    OPC_SUBUH_R_QB = (0x03 << 6) | OPC_ADDUH_QB_DSP, +    OPC_SUBQH_PH   = (0x09 << 6) | OPC_ADDUH_QB_DSP, +    OPC_SUBQH_R_PH = (0x0B << 6) | OPC_ADDUH_QB_DSP, +    OPC_SUBQH_W    = (0x11 << 6) | OPC_ADDUH_QB_DSP, +    OPC_SUBQH_R_W  = (0x13 << 6) | OPC_ADDUH_QB_DSP, +    /* MIPS DSP Multiply Sub-class insns */ +    OPC_MUL_PH     = (0x0C << 6) | OPC_ADDUH_QB_DSP, +    OPC_MUL_S_PH   = (0x0E << 6) | OPC_ADDUH_QB_DSP, +    OPC_MULQ_S_W   = (0x16 << 6) | OPC_ADDUH_QB_DSP, +    OPC_MULQ_RS_W  = (0x17 << 6) | OPC_ADDUH_QB_DSP, +}; + +#define MASK_ABSQ_S_PH(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Arithmetic Sub-class */ +    OPC_ABSQ_S_QB       = (0x01 << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_ABSQ_S_PH       = (0x09 << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_ABSQ_S_W        = (0x11 << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_PRECEQ_W_PHL    = (0x0C << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_PRECEQ_W_PHR    = (0x0D << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_PRECEQU_PH_QBL  = (0x04 << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_PRECEQU_PH_QBR  = (0x05 << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_PRECEQU_PH_QBLA = (0x06 << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_PRECEQU_PH_QBRA = (0x07 << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_PRECEU_PH_QBL   = (0x1C << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_PRECEU_PH_QBR   = (0x1D << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_PRECEU_PH_QBLA  = (0x1E << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_PRECEU_PH_QBRA  = (0x1F << 6) | OPC_ABSQ_S_PH_DSP, +    /* DSP Bit/Manipulation Sub-class */ +    OPC_BITREV          = (0x1B << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_REPL_QB         = (0x02 << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_REPLV_QB        = (0x03 << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_REPL_PH         = (0x0A << 6) | OPC_ABSQ_S_PH_DSP, +    OPC_REPLV_PH        = (0x0B << 6) | OPC_ABSQ_S_PH_DSP, +}; + +#define MASK_CMPU_EQ_QB(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Arithmetic Sub-class */ +    OPC_PRECR_QB_PH      = (0x0D << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_PRECRQ_QB_PH     = (0x0C << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_PRECR_SRA_PH_W   = (0x1E << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_PRECR_SRA_R_PH_W = (0x1F << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_PRECRQ_PH_W      = (0x14 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_PRECRQ_RS_PH_W   = (0x15 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_PRECRQU_S_QB_PH  = (0x0F << 6) | OPC_CMPU_EQ_QB_DSP, +    /* DSP Compare-Pick Sub-class */ +    OPC_CMPU_EQ_QB       = (0x00 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMPU_LT_QB       = (0x01 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMPU_LE_QB       = (0x02 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMPGU_EQ_QB      = (0x04 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMPGU_LT_QB      = (0x05 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMPGU_LE_QB      = (0x06 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMPGDU_EQ_QB     = (0x18 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMPGDU_LT_QB     = (0x19 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMPGDU_LE_QB     = (0x1A << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMP_EQ_PH        = (0x08 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMP_LT_PH        = (0x09 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_CMP_LE_PH        = (0x0A << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_PICK_QB          = (0x03 << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_PICK_PH          = (0x0B << 6) | OPC_CMPU_EQ_QB_DSP, +    OPC_PACKRL_PH        = (0x0E << 6) | OPC_CMPU_EQ_QB_DSP, +}; + +#define MASK_SHLL_QB(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP GPR-Based Shift Sub-class */ +    OPC_SHLL_QB    = (0x00 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHLLV_QB   = (0x02 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHLL_PH    = (0x08 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHLLV_PH   = (0x0A << 6) | OPC_SHLL_QB_DSP, +    OPC_SHLL_S_PH  = (0x0C << 6) | OPC_SHLL_QB_DSP, +    OPC_SHLLV_S_PH = (0x0E << 6) | OPC_SHLL_QB_DSP, +    OPC_SHLL_S_W   = (0x14 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHLLV_S_W  = (0x16 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRL_QB    = (0x01 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRLV_QB   = (0x03 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRL_PH    = (0x19 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRLV_PH   = (0x1B << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRA_QB    = (0x04 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRA_R_QB  = (0x05 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRAV_QB   = (0x06 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRAV_R_QB = (0x07 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRA_PH    = (0x09 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRAV_PH   = (0x0B << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRA_R_PH  = (0x0D << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRAV_R_PH = (0x0F << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRA_R_W   = (0x15 << 6) | OPC_SHLL_QB_DSP, +    OPC_SHRAV_R_W  = (0x17 << 6) | OPC_SHLL_QB_DSP, +}; + +#define MASK_DPA_W_PH(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Multiply Sub-class insns */ +    OPC_DPAU_H_QBL    = (0x03 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPAU_H_QBR    = (0x07 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPSU_H_QBL    = (0x0B << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPSU_H_QBR    = (0x0F << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPA_W_PH      = (0x00 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPAX_W_PH     = (0x08 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPAQ_S_W_PH   = (0x04 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPAQX_S_W_PH  = (0x18 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPAQX_SA_W_PH = (0x1A << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPS_W_PH      = (0x01 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPSX_W_PH     = (0x09 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPSQ_S_W_PH   = (0x05 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPSQX_S_W_PH  = (0x19 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPSQX_SA_W_PH = (0x1B << 6) | OPC_DPA_W_PH_DSP, +    OPC_MULSAQ_S_W_PH = (0x06 << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPAQ_SA_L_W   = (0x0C << 6) | OPC_DPA_W_PH_DSP, +    OPC_DPSQ_SA_L_W   = (0x0D << 6) | OPC_DPA_W_PH_DSP, +    OPC_MAQ_S_W_PHL   = (0x14 << 6) | OPC_DPA_W_PH_DSP, +    OPC_MAQ_S_W_PHR   = (0x16 << 6) | OPC_DPA_W_PH_DSP, +    OPC_MAQ_SA_W_PHL  = (0x10 << 6) | OPC_DPA_W_PH_DSP, +    OPC_MAQ_SA_W_PHR  = (0x12 << 6) | OPC_DPA_W_PH_DSP, +    OPC_MULSA_W_PH    = (0x02 << 6) | OPC_DPA_W_PH_DSP, +}; + +#define MASK_INSV(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* DSP Bit/Manipulation Sub-class */ +    OPC_INSV = (0x00 << 6) | OPC_INSV_DSP, +}; + +#define MASK_APPEND(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Append Sub-class */ +    OPC_APPEND  = (0x00 << 6) | OPC_APPEND_DSP, +    OPC_PREPEND = (0x01 << 6) | OPC_APPEND_DSP, +    OPC_BALIGN  = (0x10 << 6) | OPC_APPEND_DSP, +}; + +#define MASK_EXTR_W(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Accumulator and DSPControl Access Sub-class */ +    OPC_EXTR_W     = (0x00 << 6) | OPC_EXTR_W_DSP, +    OPC_EXTR_R_W   = (0x04 << 6) | OPC_EXTR_W_DSP, +    OPC_EXTR_RS_W  = (0x06 << 6) | OPC_EXTR_W_DSP, +    OPC_EXTR_S_H   = (0x0E << 6) | OPC_EXTR_W_DSP, +    OPC_EXTRV_S_H  = (0x0F << 6) | OPC_EXTR_W_DSP, +    OPC_EXTRV_W    = (0x01 << 6) | OPC_EXTR_W_DSP, +    OPC_EXTRV_R_W  = (0x05 << 6) | OPC_EXTR_W_DSP, +    OPC_EXTRV_RS_W = (0x07 << 6) | OPC_EXTR_W_DSP, +    OPC_EXTP       = (0x02 << 6) | OPC_EXTR_W_DSP, +    OPC_EXTPV      = (0x03 << 6) | OPC_EXTR_W_DSP, +    OPC_EXTPDP     = (0x0A << 6) | OPC_EXTR_W_DSP, +    OPC_EXTPDPV    = (0x0B << 6) | OPC_EXTR_W_DSP, +    OPC_SHILO      = (0x1A << 6) | OPC_EXTR_W_DSP, +    OPC_SHILOV     = (0x1B << 6) | OPC_EXTR_W_DSP, +    OPC_MTHLIP     = (0x1F << 6) | OPC_EXTR_W_DSP, +    OPC_WRDSP      = (0x13 << 6) | OPC_EXTR_W_DSP, +    OPC_RDDSP      = (0x12 << 6) | OPC_EXTR_W_DSP, +}; + +#define MASK_ABSQ_S_QH(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Arithmetic Sub-class */ +    OPC_PRECEQ_L_PWL    = (0x14 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEQ_L_PWR    = (0x15 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEQ_PW_QHL   = (0x0C << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEQ_PW_QHR   = (0x0D << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEQ_PW_QHLA  = (0x0E << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEQ_PW_QHRA  = (0x0F << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEQU_QH_OBL  = (0x04 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEQU_QH_OBR  = (0x05 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEQU_QH_OBLA = (0x06 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEQU_QH_OBRA = (0x07 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEU_QH_OBL   = (0x1C << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEU_QH_OBR   = (0x1D << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEU_QH_OBLA  = (0x1E << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_PRECEU_QH_OBRA  = (0x1F << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_ABSQ_S_OB       = (0x01 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_ABSQ_S_PW       = (0x11 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_ABSQ_S_QH       = (0x09 << 6) | OPC_ABSQ_S_QH_DSP, +    /* DSP Bit/Manipulation Sub-class */ +    OPC_REPL_OB         = (0x02 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_REPL_PW         = (0x12 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_REPL_QH         = (0x0A << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_REPLV_OB        = (0x03 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_REPLV_PW        = (0x13 << 6) | OPC_ABSQ_S_QH_DSP, +    OPC_REPLV_QH        = (0x0B << 6) | OPC_ABSQ_S_QH_DSP, +}; + +#define MASK_ADDU_OB(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Multiply Sub-class insns */ +    OPC_MULEQ_S_PW_QHL = (0x1C << 6) | OPC_ADDU_OB_DSP, +    OPC_MULEQ_S_PW_QHR = (0x1D << 6) | OPC_ADDU_OB_DSP, +    OPC_MULEU_S_QH_OBL = (0x06 << 6) | OPC_ADDU_OB_DSP, +    OPC_MULEU_S_QH_OBR = (0x07 << 6) | OPC_ADDU_OB_DSP, +    OPC_MULQ_RS_QH     = (0x1F << 6) | OPC_ADDU_OB_DSP, +    /* MIPS DSP Arithmetic Sub-class */ +    OPC_RADDU_L_OB     = (0x14 << 6) | OPC_ADDU_OB_DSP, +    OPC_SUBQ_PW        = (0x13 << 6) | OPC_ADDU_OB_DSP, +    OPC_SUBQ_S_PW      = (0x17 << 6) | OPC_ADDU_OB_DSP, +    OPC_SUBQ_QH        = (0x0B << 6) | OPC_ADDU_OB_DSP, +    OPC_SUBQ_S_QH      = (0x0F << 6) | OPC_ADDU_OB_DSP, +    OPC_SUBU_OB        = (0x01 << 6) | OPC_ADDU_OB_DSP, +    OPC_SUBU_S_OB      = (0x05 << 6) | OPC_ADDU_OB_DSP, +    OPC_SUBU_QH        = (0x09 << 6) | OPC_ADDU_OB_DSP, +    OPC_SUBU_S_QH      = (0x0D << 6) | OPC_ADDU_OB_DSP, +    OPC_SUBUH_OB       = (0x19 << 6) | OPC_ADDU_OB_DSP, +    OPC_SUBUH_R_OB     = (0x1B << 6) | OPC_ADDU_OB_DSP, +    OPC_ADDQ_PW        = (0x12 << 6) | OPC_ADDU_OB_DSP, +    OPC_ADDQ_S_PW      = (0x16 << 6) | OPC_ADDU_OB_DSP, +    OPC_ADDQ_QH        = (0x0A << 6) | OPC_ADDU_OB_DSP, +    OPC_ADDQ_S_QH      = (0x0E << 6) | OPC_ADDU_OB_DSP, +    OPC_ADDU_OB        = (0x00 << 6) | OPC_ADDU_OB_DSP, +    OPC_ADDU_S_OB      = (0x04 << 6) | OPC_ADDU_OB_DSP, +    OPC_ADDU_QH        = (0x08 << 6) | OPC_ADDU_OB_DSP, +    OPC_ADDU_S_QH      = (0x0C << 6) | OPC_ADDU_OB_DSP, +    OPC_ADDUH_OB       = (0x18 << 6) | OPC_ADDU_OB_DSP, +    OPC_ADDUH_R_OB     = (0x1A << 6) | OPC_ADDU_OB_DSP, +}; + +#define MASK_CMPU_EQ_OB(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* DSP Compare-Pick Sub-class */ +    OPC_CMP_EQ_PW         = (0x10 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMP_LT_PW         = (0x11 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMP_LE_PW         = (0x12 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMP_EQ_QH         = (0x08 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMP_LT_QH         = (0x09 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMP_LE_QH         = (0x0A << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMPGDU_EQ_OB      = (0x18 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMPGDU_LT_OB      = (0x19 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMPGDU_LE_OB      = (0x1A << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMPGU_EQ_OB       = (0x04 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMPGU_LT_OB       = (0x05 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMPGU_LE_OB       = (0x06 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMPU_EQ_OB        = (0x00 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMPU_LT_OB        = (0x01 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_CMPU_LE_OB        = (0x02 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PACKRL_PW         = (0x0E << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PICK_OB           = (0x03 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PICK_PW           = (0x13 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PICK_QH           = (0x0B << 6) | OPC_CMPU_EQ_OB_DSP, +    /* MIPS DSP Arithmetic Sub-class */ +    OPC_PRECR_OB_QH       = (0x0D << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PRECR_SRA_QH_PW   = (0x1E << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PRECR_SRA_R_QH_PW = (0x1F << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PRECRQ_OB_QH      = (0x0C << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PRECRQ_PW_L       = (0x1C << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PRECRQ_QH_PW      = (0x14 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PRECRQ_RS_QH_PW   = (0x15 << 6) | OPC_CMPU_EQ_OB_DSP, +    OPC_PRECRQU_S_OB_QH   = (0x0F << 6) | OPC_CMPU_EQ_OB_DSP, +}; + +#define MASK_DAPPEND(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* DSP Append Sub-class */ +    OPC_DAPPEND  = (0x00 << 6) | OPC_DAPPEND_DSP, +    OPC_PREPENDD = (0x03 << 6) | OPC_DAPPEND_DSP, +    OPC_PREPENDW = (0x01 << 6) | OPC_DAPPEND_DSP, +    OPC_DBALIGN  = (0x10 << 6) | OPC_DAPPEND_DSP, +}; + +#define MASK_DEXTR_W(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Accumulator and DSPControl Access Sub-class */ +    OPC_DMTHLIP     = (0x1F << 6) | OPC_DEXTR_W_DSP, +    OPC_DSHILO      = (0x1A << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTP       = (0x02 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTPDP     = (0x0A << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTPDPV    = (0x0B << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTPV      = (0x03 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTR_L     = (0x10 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTR_R_L   = (0x14 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTR_RS_L  = (0x16 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTR_W     = (0x00 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTR_R_W   = (0x04 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTR_RS_W  = (0x06 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTR_S_H   = (0x0E << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTRV_L    = (0x11 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTRV_R_L  = (0x15 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTRV_RS_L = (0x17 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTRV_S_H  = (0x0F << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTRV_W    = (0x01 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTRV_R_W  = (0x05 << 6) | OPC_DEXTR_W_DSP, +    OPC_DEXTRV_RS_W = (0x07 << 6) | OPC_DEXTR_W_DSP, +    OPC_DSHILOV     = (0x1B << 6) | OPC_DEXTR_W_DSP, +}; + +#define MASK_DINSV(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* DSP Bit/Manipulation Sub-class */ +    OPC_DINSV = (0x00 << 6) | OPC_DINSV_DSP, +}; + +#define MASK_DPAQ_W_QH(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP Multiply Sub-class insns */ +    OPC_DMADD         = (0x19 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DMADDU        = (0x1D << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DMSUB         = (0x1B << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DMSUBU        = (0x1F << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DPA_W_QH      = (0x00 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DPAQ_S_W_QH   = (0x04 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DPAQ_SA_L_PW  = (0x0C << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DPAU_H_OBL    = (0x03 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DPAU_H_OBR    = (0x07 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DPS_W_QH      = (0x01 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DPSQ_S_W_QH   = (0x05 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DPSQ_SA_L_PW  = (0x0D << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DPSU_H_OBL    = (0x0B << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_DPSU_H_OBR    = (0x0F << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MAQ_S_L_PWL   = (0x1C << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MAQ_S_L_PWR   = (0x1E << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MAQ_S_W_QHLL  = (0x14 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MAQ_SA_W_QHLL = (0x10 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MAQ_S_W_QHLR  = (0x15 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MAQ_SA_W_QHLR = (0x11 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MAQ_S_W_QHRL  = (0x16 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MAQ_SA_W_QHRL = (0x12 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MAQ_S_W_QHRR  = (0x17 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MAQ_SA_W_QHRR = (0x13 << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MULSAQ_S_L_PW = (0x0E << 6) | OPC_DPAQ_W_QH_DSP, +    OPC_MULSAQ_S_W_QH = (0x06 << 6) | OPC_DPAQ_W_QH_DSP, +}; + +#define MASK_SHLL_OB(op) (MASK_SPECIAL3(op) | (op & (0x1F << 6))) +enum { +    /* MIPS DSP GPR-Based Shift Sub-class */ +    OPC_SHLL_PW    = (0x10 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHLL_S_PW  = (0x14 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHLLV_OB   = (0x02 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHLLV_PW   = (0x12 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHLLV_S_PW = (0x16 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHLLV_QH   = (0x0A << 6) | OPC_SHLL_OB_DSP, +    OPC_SHLLV_S_QH = (0x0E << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRA_PW    = (0x11 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRA_R_PW  = (0x15 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRAV_OB   = (0x06 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRAV_R_OB = (0x07 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRAV_PW   = (0x13 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRAV_R_PW = (0x17 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRAV_QH   = (0x0B << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRAV_R_QH = (0x0F << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRLV_OB   = (0x03 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRLV_QH   = (0x1B << 6) | OPC_SHLL_OB_DSP, +    OPC_SHLL_OB    = (0x00 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHLL_QH    = (0x08 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHLL_S_QH  = (0x0C << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRA_OB    = (0x04 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRA_R_OB  = (0x05 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRA_QH    = (0x09 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRA_R_QH  = (0x0D << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRL_OB    = (0x01 << 6) | OPC_SHLL_OB_DSP, +    OPC_SHRL_QH    = (0x19 << 6) | OPC_SHLL_OB_DSP, +}; + +/* Coprocessor 0 (rs field) */ +#define MASK_CP0(op)       MASK_OP_MAJOR(op) | (op & (0x1F << 21)) + +enum { +    OPC_MFC0     = (0x00 << 21) | OPC_CP0, +    OPC_DMFC0    = (0x01 << 21) | OPC_CP0, +    OPC_MFHC0    = (0x02 << 21) | OPC_CP0, +    OPC_MTC0     = (0x04 << 21) | OPC_CP0, +    OPC_DMTC0    = (0x05 << 21) | OPC_CP0, +    OPC_MTHC0    = (0x06 << 21) | OPC_CP0, +    OPC_MFTR     = (0x08 << 21) | OPC_CP0, +    OPC_RDPGPR   = (0x0A << 21) | OPC_CP0, +    OPC_MFMC0    = (0x0B << 21) | OPC_CP0, +    OPC_MTTR     = (0x0C << 21) | OPC_CP0, +    OPC_WRPGPR   = (0x0E << 21) | OPC_CP0, +    OPC_C0       = (0x10 << 21) | OPC_CP0, +    OPC_C0_FIRST = (0x10 << 21) | OPC_CP0, +    OPC_C0_LAST  = (0x1F << 21) | OPC_CP0, +}; + +/* MFMC0 opcodes */ +#define MASK_MFMC0(op)     MASK_CP0(op) | (op & 0xFFFF) + +enum { +    OPC_DMT      = 0x01 | (0 << 5) | (0x0F << 6) | (0x01 << 11) | OPC_MFMC0, +    OPC_EMT      = 0x01 | (1 << 5) | (0x0F << 6) | (0x01 << 11) | OPC_MFMC0, +    OPC_DVPE     = 0x01 | (0 << 5) | OPC_MFMC0, +    OPC_EVPE     = 0x01 | (1 << 5) | OPC_MFMC0, +    OPC_DI       = (0 << 5) | (0x0C << 11) | OPC_MFMC0, +    OPC_EI       = (1 << 5) | (0x0C << 11) | OPC_MFMC0, +}; + +/* Coprocessor 0 (with rs == C0) */ +#define MASK_C0(op)        MASK_CP0(op) | (op & 0x3F) + +enum { +    OPC_TLBR     = 0x01 | OPC_C0, +    OPC_TLBWI    = 0x02 | OPC_C0, +    OPC_TLBINV   = 0x03 | OPC_C0, +    OPC_TLBINVF  = 0x04 | OPC_C0, +    OPC_TLBWR    = 0x06 | OPC_C0, +    OPC_TLBP     = 0x08 | OPC_C0, +    OPC_RFE      = 0x10 | OPC_C0, +    OPC_ERET     = 0x18 | OPC_C0, +    OPC_DERET    = 0x1F | OPC_C0, +    OPC_WAIT     = 0x20 | OPC_C0, +}; + +/* Coprocessor 1 (rs field) */ +#define MASK_CP1(op)       MASK_OP_MAJOR(op) | (op & (0x1F << 21)) + +/* Values for the fmt field in FP instructions */ +enum { +    /* 0 - 15 are reserved */ +    FMT_S = 16,          /* single fp */ +    FMT_D = 17,          /* double fp */ +    FMT_E = 18,          /* extended fp */ +    FMT_Q = 19,          /* quad fp */ +    FMT_W = 20,          /* 32-bit fixed */ +    FMT_L = 21,          /* 64-bit fixed */ +    FMT_PS = 22,         /* paired single fp */ +    /* 23 - 31 are reserved */ +}; + +enum { +    OPC_MFC1     = (0x00 << 21) | OPC_CP1, +    OPC_DMFC1    = (0x01 << 21) | OPC_CP1, +    OPC_CFC1     = (0x02 << 21) | OPC_CP1, +    OPC_MFHC1    = (0x03 << 21) | OPC_CP1, +    OPC_MTC1     = (0x04 << 21) | OPC_CP1, +    OPC_DMTC1    = (0x05 << 21) | OPC_CP1, +    OPC_CTC1     = (0x06 << 21) | OPC_CP1, +    OPC_MTHC1    = (0x07 << 21) | OPC_CP1, +    OPC_BC1      = (0x08 << 21) | OPC_CP1, /* bc */ +    OPC_BC1ANY2  = (0x09 << 21) | OPC_CP1, +    OPC_BC1ANY4  = (0x0A << 21) | OPC_CP1, +    OPC_BZ_V     = (0x0B << 21) | OPC_CP1, +    OPC_BNZ_V    = (0x0F << 21) | OPC_CP1, +    OPC_S_FMT    = (FMT_S << 21) | OPC_CP1, +    OPC_D_FMT    = (FMT_D << 21) | OPC_CP1, +    OPC_E_FMT    = (FMT_E << 21) | OPC_CP1, +    OPC_Q_FMT    = (FMT_Q << 21) | OPC_CP1, +    OPC_W_FMT    = (FMT_W << 21) | OPC_CP1, +    OPC_L_FMT    = (FMT_L << 21) | OPC_CP1, +    OPC_PS_FMT   = (FMT_PS << 21) | OPC_CP1, +    OPC_BC1EQZ   = (0x09 << 21) | OPC_CP1, +    OPC_BC1NEZ   = (0x0D << 21) | OPC_CP1, +    OPC_BZ_B     = (0x18 << 21) | OPC_CP1, +    OPC_BZ_H     = (0x19 << 21) | OPC_CP1, +    OPC_BZ_W     = (0x1A << 21) | OPC_CP1, +    OPC_BZ_D     = (0x1B << 21) | OPC_CP1, +    OPC_BNZ_B    = (0x1C << 21) | OPC_CP1, +    OPC_BNZ_H    = (0x1D << 21) | OPC_CP1, +    OPC_BNZ_W    = (0x1E << 21) | OPC_CP1, +    OPC_BNZ_D    = (0x1F << 21) | OPC_CP1, +}; + +#define MASK_CP1_FUNC(op)       MASK_CP1(op) | (op & 0x3F) +#define MASK_BC1(op)            MASK_CP1(op) | (op & (0x3 << 16)) + +enum { +    OPC_BC1F     = (0x00 << 16) | OPC_BC1, +    OPC_BC1T     = (0x01 << 16) | OPC_BC1, +    OPC_BC1FL    = (0x02 << 16) | OPC_BC1, +    OPC_BC1TL    = (0x03 << 16) | OPC_BC1, +}; + +enum { +    OPC_BC1FANY2     = (0x00 << 16) | OPC_BC1ANY2, +    OPC_BC1TANY2     = (0x01 << 16) | OPC_BC1ANY2, +}; + +enum { +    OPC_BC1FANY4     = (0x00 << 16) | OPC_BC1ANY4, +    OPC_BC1TANY4     = (0x01 << 16) | OPC_BC1ANY4, +}; + +#define MASK_CP2(op)       MASK_OP_MAJOR(op) | (op & (0x1F << 21)) + +enum { +    OPC_MFC2    = (0x00 << 21) | OPC_CP2, +    OPC_DMFC2   = (0x01 << 21) | OPC_CP2, +    OPC_CFC2    = (0x02 << 21) | OPC_CP2, +    OPC_MFHC2   = (0x03 << 21) | OPC_CP2, +    OPC_MTC2    = (0x04 << 21) | OPC_CP2, +    OPC_DMTC2   = (0x05 << 21) | OPC_CP2, +    OPC_CTC2    = (0x06 << 21) | OPC_CP2, +    OPC_MTHC2   = (0x07 << 21) | OPC_CP2, +    OPC_BC2     = (0x08 << 21) | OPC_CP2, +    OPC_BC2EQZ  = (0x09 << 21) | OPC_CP2, +    OPC_BC2NEZ  = (0x0D << 21) | OPC_CP2, +}; + +#define MASK_LMI(op)  (MASK_OP_MAJOR(op) | (op & (0x1F << 21)) | (op & 0x1F)) + +enum { +    OPC_PADDSH  = (24 << 21) | (0x00) | OPC_CP2, +    OPC_PADDUSH = (25 << 21) | (0x00) | OPC_CP2, +    OPC_PADDH   = (26 << 21) | (0x00) | OPC_CP2, +    OPC_PADDW   = (27 << 21) | (0x00) | OPC_CP2, +    OPC_PADDSB  = (28 << 21) | (0x00) | OPC_CP2, +    OPC_PADDUSB = (29 << 21) | (0x00) | OPC_CP2, +    OPC_PADDB   = (30 << 21) | (0x00) | OPC_CP2, +    OPC_PADDD   = (31 << 21) | (0x00) | OPC_CP2, + +    OPC_PSUBSH  = (24 << 21) | (0x01) | OPC_CP2, +    OPC_PSUBUSH = (25 << 21) | (0x01) | OPC_CP2, +    OPC_PSUBH   = (26 << 21) | (0x01) | OPC_CP2, +    OPC_PSUBW   = (27 << 21) | (0x01) | OPC_CP2, +    OPC_PSUBSB  = (28 << 21) | (0x01) | OPC_CP2, +    OPC_PSUBUSB = (29 << 21) | (0x01) | OPC_CP2, +    OPC_PSUBB   = (30 << 21) | (0x01) | OPC_CP2, +    OPC_PSUBD   = (31 << 21) | (0x01) | OPC_CP2, + +    OPC_PSHUFH   = (24 << 21) | (0x02) | OPC_CP2, +    OPC_PACKSSWH = (25 << 21) | (0x02) | OPC_CP2, +    OPC_PACKSSHB = (26 << 21) | (0x02) | OPC_CP2, +    OPC_PACKUSHB = (27 << 21) | (0x02) | OPC_CP2, +    OPC_XOR_CP2  = (28 << 21) | (0x02) | OPC_CP2, +    OPC_NOR_CP2  = (29 << 21) | (0x02) | OPC_CP2, +    OPC_AND_CP2  = (30 << 21) | (0x02) | OPC_CP2, +    OPC_PANDN    = (31 << 21) | (0x02) | OPC_CP2, + +    OPC_PUNPCKLHW = (24 << 21) | (0x03) | OPC_CP2, +    OPC_PUNPCKHHW = (25 << 21) | (0x03) | OPC_CP2, +    OPC_PUNPCKLBH = (26 << 21) | (0x03) | OPC_CP2, +    OPC_PUNPCKHBH = (27 << 21) | (0x03) | OPC_CP2, +    OPC_PINSRH_0  = (28 << 21) | (0x03) | OPC_CP2, +    OPC_PINSRH_1  = (29 << 21) | (0x03) | OPC_CP2, +    OPC_PINSRH_2  = (30 << 21) | (0x03) | OPC_CP2, +    OPC_PINSRH_3  = (31 << 21) | (0x03) | OPC_CP2, + +    OPC_PAVGH   = (24 << 21) | (0x08) | OPC_CP2, +    OPC_PAVGB   = (25 << 21) | (0x08) | OPC_CP2, +    OPC_PMAXSH  = (26 << 21) | (0x08) | OPC_CP2, +    OPC_PMINSH  = (27 << 21) | (0x08) | OPC_CP2, +    OPC_PMAXUB  = (28 << 21) | (0x08) | OPC_CP2, +    OPC_PMINUB  = (29 << 21) | (0x08) | OPC_CP2, + +    OPC_PCMPEQW = (24 << 21) | (0x09) | OPC_CP2, +    OPC_PCMPGTW = (25 << 21) | (0x09) | OPC_CP2, +    OPC_PCMPEQH = (26 << 21) | (0x09) | OPC_CP2, +    OPC_PCMPGTH = (27 << 21) | (0x09) | OPC_CP2, +    OPC_PCMPEQB = (28 << 21) | (0x09) | OPC_CP2, +    OPC_PCMPGTB = (29 << 21) | (0x09) | OPC_CP2, + +    OPC_PSLLW   = (24 << 21) | (0x0A) | OPC_CP2, +    OPC_PSLLH   = (25 << 21) | (0x0A) | OPC_CP2, +    OPC_PMULLH  = (26 << 21) | (0x0A) | OPC_CP2, +    OPC_PMULHH  = (27 << 21) | (0x0A) | OPC_CP2, +    OPC_PMULUW  = (28 << 21) | (0x0A) | OPC_CP2, +    OPC_PMULHUH = (29 << 21) | (0x0A) | OPC_CP2, + +    OPC_PSRLW     = (24 << 21) | (0x0B) | OPC_CP2, +    OPC_PSRLH     = (25 << 21) | (0x0B) | OPC_CP2, +    OPC_PSRAW     = (26 << 21) | (0x0B) | OPC_CP2, +    OPC_PSRAH     = (27 << 21) | (0x0B) | OPC_CP2, +    OPC_PUNPCKLWD = (28 << 21) | (0x0B) | OPC_CP2, +    OPC_PUNPCKHWD = (29 << 21) | (0x0B) | OPC_CP2, + +    OPC_ADDU_CP2 = (24 << 21) | (0x0C) | OPC_CP2, +    OPC_OR_CP2   = (25 << 21) | (0x0C) | OPC_CP2, +    OPC_ADD_CP2  = (26 << 21) | (0x0C) | OPC_CP2, +    OPC_DADD_CP2 = (27 << 21) | (0x0C) | OPC_CP2, +    OPC_SEQU_CP2 = (28 << 21) | (0x0C) | OPC_CP2, +    OPC_SEQ_CP2  = (29 << 21) | (0x0C) | OPC_CP2, + +    OPC_SUBU_CP2 = (24 << 21) | (0x0D) | OPC_CP2, +    OPC_PASUBUB  = (25 << 21) | (0x0D) | OPC_CP2, +    OPC_SUB_CP2  = (26 << 21) | (0x0D) | OPC_CP2, +    OPC_DSUB_CP2 = (27 << 21) | (0x0D) | OPC_CP2, +    OPC_SLTU_CP2 = (28 << 21) | (0x0D) | OPC_CP2, +    OPC_SLT_CP2  = (29 << 21) | (0x0D) | OPC_CP2, + +    OPC_SLL_CP2  = (24 << 21) | (0x0E) | OPC_CP2, +    OPC_DSLL_CP2 = (25 << 21) | (0x0E) | OPC_CP2, +    OPC_PEXTRH   = (26 << 21) | (0x0E) | OPC_CP2, +    OPC_PMADDHW  = (27 << 21) | (0x0E) | OPC_CP2, +    OPC_SLEU_CP2 = (28 << 21) | (0x0E) | OPC_CP2, +    OPC_SLE_CP2  = (29 << 21) | (0x0E) | OPC_CP2, + +    OPC_SRL_CP2  = (24 << 21) | (0x0F) | OPC_CP2, +    OPC_DSRL_CP2 = (25 << 21) | (0x0F) | OPC_CP2, +    OPC_SRA_CP2  = (26 << 21) | (0x0F) | OPC_CP2, +    OPC_DSRA_CP2 = (27 << 21) | (0x0F) | OPC_CP2, +    OPC_BIADD    = (28 << 21) | (0x0F) | OPC_CP2, +    OPC_PMOVMSKB = (29 << 21) | (0x0F) | OPC_CP2, +}; + + +#define MASK_CP3(op)       MASK_OP_MAJOR(op) | (op & 0x3F) + +enum { +    OPC_LWXC1   = 0x00 | OPC_CP3, +    OPC_LDXC1   = 0x01 | OPC_CP3, +    OPC_LUXC1   = 0x05 | OPC_CP3, +    OPC_SWXC1   = 0x08 | OPC_CP3, +    OPC_SDXC1   = 0x09 | OPC_CP3, +    OPC_SUXC1   = 0x0D | OPC_CP3, +    OPC_PREFX   = 0x0F | OPC_CP3, +    OPC_ALNV_PS = 0x1E | OPC_CP3, +    OPC_MADD_S  = 0x20 | OPC_CP3, +    OPC_MADD_D  = 0x21 | OPC_CP3, +    OPC_MADD_PS = 0x26 | OPC_CP3, +    OPC_MSUB_S  = 0x28 | OPC_CP3, +    OPC_MSUB_D  = 0x29 | OPC_CP3, +    OPC_MSUB_PS = 0x2E | OPC_CP3, +    OPC_NMADD_S = 0x30 | OPC_CP3, +    OPC_NMADD_D = 0x31 | OPC_CP3, +    OPC_NMADD_PS= 0x36 | OPC_CP3, +    OPC_NMSUB_S = 0x38 | OPC_CP3, +    OPC_NMSUB_D = 0x39 | OPC_CP3, +    OPC_NMSUB_PS= 0x3E | OPC_CP3, +}; + +/* MSA Opcodes */ +#define MASK_MSA_MINOR(op)    (MASK_OP_MAJOR(op) | (op & 0x3F)) +enum { +    OPC_MSA_I8_00   = 0x00 | OPC_MSA, +    OPC_MSA_I8_01   = 0x01 | OPC_MSA, +    OPC_MSA_I8_02   = 0x02 | OPC_MSA, +    OPC_MSA_I5_06   = 0x06 | OPC_MSA, +    OPC_MSA_I5_07   = 0x07 | OPC_MSA, +    OPC_MSA_BIT_09  = 0x09 | OPC_MSA, +    OPC_MSA_BIT_0A  = 0x0A | OPC_MSA, +    OPC_MSA_3R_0D   = 0x0D | OPC_MSA, +    OPC_MSA_3R_0E   = 0x0E | OPC_MSA, +    OPC_MSA_3R_0F   = 0x0F | OPC_MSA, +    OPC_MSA_3R_10   = 0x10 | OPC_MSA, +    OPC_MSA_3R_11   = 0x11 | OPC_MSA, +    OPC_MSA_3R_12   = 0x12 | OPC_MSA, +    OPC_MSA_3R_13   = 0x13 | OPC_MSA, +    OPC_MSA_3R_14   = 0x14 | OPC_MSA, +    OPC_MSA_3R_15   = 0x15 | OPC_MSA, +    OPC_MSA_ELM     = 0x19 | OPC_MSA, +    OPC_MSA_3RF_1A  = 0x1A | OPC_MSA, +    OPC_MSA_3RF_1B  = 0x1B | OPC_MSA, +    OPC_MSA_3RF_1C  = 0x1C | OPC_MSA, +    OPC_MSA_VEC     = 0x1E | OPC_MSA, + +    /* MI10 instruction */ +    OPC_LD_B    = (0x20) | OPC_MSA, +    OPC_LD_H    = (0x21) | OPC_MSA, +    OPC_LD_W    = (0x22) | OPC_MSA, +    OPC_LD_D    = (0x23) | OPC_MSA, +    OPC_ST_B    = (0x24) | OPC_MSA, +    OPC_ST_H    = (0x25) | OPC_MSA, +    OPC_ST_W    = (0x26) | OPC_MSA, +    OPC_ST_D    = (0x27) | OPC_MSA, +}; + +enum { +    /* I5 instruction df(bits 22..21) = _b, _h, _w, _d */ +    OPC_ADDVI_df    = (0x0 << 23) | OPC_MSA_I5_06, +    OPC_CEQI_df     = (0x0 << 23) | OPC_MSA_I5_07, +    OPC_SUBVI_df    = (0x1 << 23) | OPC_MSA_I5_06, +    OPC_MAXI_S_df   = (0x2 << 23) | OPC_MSA_I5_06, +    OPC_CLTI_S_df   = (0x2 << 23) | OPC_MSA_I5_07, +    OPC_MAXI_U_df   = (0x3 << 23) | OPC_MSA_I5_06, +    OPC_CLTI_U_df   = (0x3 << 23) | OPC_MSA_I5_07, +    OPC_MINI_S_df   = (0x4 << 23) | OPC_MSA_I5_06, +    OPC_CLEI_S_df   = (0x4 << 23) | OPC_MSA_I5_07, +    OPC_MINI_U_df   = (0x5 << 23) | OPC_MSA_I5_06, +    OPC_CLEI_U_df   = (0x5 << 23) | OPC_MSA_I5_07, +    OPC_LDI_df      = (0x6 << 23) | OPC_MSA_I5_07, + +    /* I8 instruction */ +    OPC_ANDI_B  = (0x0 << 24) | OPC_MSA_I8_00, +    OPC_BMNZI_B = (0x0 << 24) | OPC_MSA_I8_01, +    OPC_SHF_B   = (0x0 << 24) | OPC_MSA_I8_02, +    OPC_ORI_B   = (0x1 << 24) | OPC_MSA_I8_00, +    OPC_BMZI_B  = (0x1 << 24) | OPC_MSA_I8_01, +    OPC_SHF_H   = (0x1 << 24) | OPC_MSA_I8_02, +    OPC_NORI_B  = (0x2 << 24) | OPC_MSA_I8_00, +    OPC_BSELI_B = (0x2 << 24) | OPC_MSA_I8_01, +    OPC_SHF_W   = (0x2 << 24) | OPC_MSA_I8_02, +    OPC_XORI_B  = (0x3 << 24) | OPC_MSA_I8_00, + +    /* VEC/2R/2RF instruction */ +    OPC_AND_V   = (0x00 << 21) | OPC_MSA_VEC, +    OPC_OR_V    = (0x01 << 21) | OPC_MSA_VEC, +    OPC_NOR_V   = (0x02 << 21) | OPC_MSA_VEC, +    OPC_XOR_V   = (0x03 << 21) | OPC_MSA_VEC, +    OPC_BMNZ_V  = (0x04 << 21) | OPC_MSA_VEC, +    OPC_BMZ_V   = (0x05 << 21) | OPC_MSA_VEC, +    OPC_BSEL_V  = (0x06 << 21) | OPC_MSA_VEC, + +    OPC_MSA_2R      = (0x18 << 21) | OPC_MSA_VEC, +    OPC_MSA_2RF     = (0x19 << 21) | OPC_MSA_VEC, + +    /* 2R instruction df(bits 17..16) = _b, _h, _w, _d */ +    OPC_FILL_df = (0x00 << 18) | OPC_MSA_2R, +    OPC_PCNT_df = (0x01 << 18) | OPC_MSA_2R, +    OPC_NLOC_df = (0x02 << 18) | OPC_MSA_2R, +    OPC_NLZC_df = (0x03 << 18) | OPC_MSA_2R, + +    /* 2RF instruction df(bit 16) = _w, _d */ +    OPC_FCLASS_df   = (0x00 << 17) | OPC_MSA_2RF, +    OPC_FTRUNC_S_df = (0x01 << 17) | OPC_MSA_2RF, +    OPC_FTRUNC_U_df = (0x02 << 17) | OPC_MSA_2RF, +    OPC_FSQRT_df    = (0x03 << 17) | OPC_MSA_2RF, +    OPC_FRSQRT_df   = (0x04 << 17) | OPC_MSA_2RF, +    OPC_FRCP_df     = (0x05 << 17) | OPC_MSA_2RF, +    OPC_FRINT_df    = (0x06 << 17) | OPC_MSA_2RF, +    OPC_FLOG2_df    = (0x07 << 17) | OPC_MSA_2RF, +    OPC_FEXUPL_df   = (0x08 << 17) | OPC_MSA_2RF, +    OPC_FEXUPR_df   = (0x09 << 17) | OPC_MSA_2RF, +    OPC_FFQL_df     = (0x0A << 17) | OPC_MSA_2RF, +    OPC_FFQR_df     = (0x0B << 17) | OPC_MSA_2RF, +    OPC_FTINT_S_df  = (0x0C << 17) | OPC_MSA_2RF, +    OPC_FTINT_U_df  = (0x0D << 17) | OPC_MSA_2RF, +    OPC_FFINT_S_df  = (0x0E << 17) | OPC_MSA_2RF, +    OPC_FFINT_U_df  = (0x0F << 17) | OPC_MSA_2RF, + +    /* 3R instruction df(bits 22..21) = _b, _h, _w, d */ +    OPC_SLL_df      = (0x0 << 23) | OPC_MSA_3R_0D, +    OPC_ADDV_df     = (0x0 << 23) | OPC_MSA_3R_0E, +    OPC_CEQ_df      = (0x0 << 23) | OPC_MSA_3R_0F, +    OPC_ADD_A_df    = (0x0 << 23) | OPC_MSA_3R_10, +    OPC_SUBS_S_df   = (0x0 << 23) | OPC_MSA_3R_11, +    OPC_MULV_df     = (0x0 << 23) | OPC_MSA_3R_12, +    OPC_DOTP_S_df   = (0x0 << 23) | OPC_MSA_3R_13, +    OPC_SLD_df      = (0x0 << 23) | OPC_MSA_3R_14, +    OPC_VSHF_df     = (0x0 << 23) | OPC_MSA_3R_15, +    OPC_SRA_df      = (0x1 << 23) | OPC_MSA_3R_0D, +    OPC_SUBV_df     = (0x1 << 23) | OPC_MSA_3R_0E, +    OPC_ADDS_A_df   = (0x1 << 23) | OPC_MSA_3R_10, +    OPC_SUBS_U_df   = (0x1 << 23) | OPC_MSA_3R_11, +    OPC_MADDV_df    = (0x1 << 23) | OPC_MSA_3R_12, +    OPC_DOTP_U_df   = (0x1 << 23) | OPC_MSA_3R_13, +    OPC_SPLAT_df    = (0x1 << 23) | OPC_MSA_3R_14, +    OPC_SRAR_df     = (0x1 << 23) | OPC_MSA_3R_15, +    OPC_SRL_df      = (0x2 << 23) | OPC_MSA_3R_0D, +    OPC_MAX_S_df    = (0x2 << 23) | OPC_MSA_3R_0E, +    OPC_CLT_S_df    = (0x2 << 23) | OPC_MSA_3R_0F, +    OPC_ADDS_S_df   = (0x2 << 23) | OPC_MSA_3R_10, +    OPC_SUBSUS_U_df = (0x2 << 23) | OPC_MSA_3R_11, +    OPC_MSUBV_df    = (0x2 << 23) | OPC_MSA_3R_12, +    OPC_DPADD_S_df  = (0x2 << 23) | OPC_MSA_3R_13, +    OPC_PCKEV_df    = (0x2 << 23) | OPC_MSA_3R_14, +    OPC_SRLR_df     = (0x2 << 23) | OPC_MSA_3R_15, +    OPC_BCLR_df     = (0x3 << 23) | OPC_MSA_3R_0D, +    OPC_MAX_U_df    = (0x3 << 23) | OPC_MSA_3R_0E, +    OPC_CLT_U_df    = (0x3 << 23) | OPC_MSA_3R_0F, +    OPC_ADDS_U_df   = (0x3 << 23) | OPC_MSA_3R_10, +    OPC_SUBSUU_S_df = (0x3 << 23) | OPC_MSA_3R_11, +    OPC_DPADD_U_df  = (0x3 << 23) | OPC_MSA_3R_13, +    OPC_PCKOD_df    = (0x3 << 23) | OPC_MSA_3R_14, +    OPC_BSET_df     = (0x4 << 23) | OPC_MSA_3R_0D, +    OPC_MIN_S_df    = (0x4 << 23) | OPC_MSA_3R_0E, +    OPC_CLE_S_df    = (0x4 << 23) | OPC_MSA_3R_0F, +    OPC_AVE_S_df    = (0x4 << 23) | OPC_MSA_3R_10, +    OPC_ASUB_S_df   = (0x4 << 23) | OPC_MSA_3R_11, +    OPC_DIV_S_df    = (0x4 << 23) | OPC_MSA_3R_12, +    OPC_DPSUB_S_df  = (0x4 << 23) | OPC_MSA_3R_13, +    OPC_ILVL_df     = (0x4 << 23) | OPC_MSA_3R_14, +    OPC_HADD_S_df   = (0x4 << 23) | OPC_MSA_3R_15, +    OPC_BNEG_df     = (0x5 << 23) | OPC_MSA_3R_0D, +    OPC_MIN_U_df    = (0x5 << 23) | OPC_MSA_3R_0E, +    OPC_CLE_U_df    = (0x5 << 23) | OPC_MSA_3R_0F, +    OPC_AVE_U_df    = (0x5 << 23) | OPC_MSA_3R_10, +    OPC_ASUB_U_df   = (0x5 << 23) | OPC_MSA_3R_11, +    OPC_DIV_U_df    = (0x5 << 23) | OPC_MSA_3R_12, +    OPC_DPSUB_U_df  = (0x5 << 23) | OPC_MSA_3R_13, +    OPC_ILVR_df     = (0x5 << 23) | OPC_MSA_3R_14, +    OPC_HADD_U_df   = (0x5 << 23) | OPC_MSA_3R_15, +    OPC_BINSL_df    = (0x6 << 23) | OPC_MSA_3R_0D, +    OPC_MAX_A_df    = (0x6 << 23) | OPC_MSA_3R_0E, +    OPC_AVER_S_df   = (0x6 << 23) | OPC_MSA_3R_10, +    OPC_MOD_S_df    = (0x6 << 23) | OPC_MSA_3R_12, +    OPC_ILVEV_df    = (0x6 << 23) | OPC_MSA_3R_14, +    OPC_HSUB_S_df   = (0x6 << 23) | OPC_MSA_3R_15, +    OPC_BINSR_df    = (0x7 << 23) | OPC_MSA_3R_0D, +    OPC_MIN_A_df    = (0x7 << 23) | OPC_MSA_3R_0E, +    OPC_AVER_U_df   = (0x7 << 23) | OPC_MSA_3R_10, +    OPC_MOD_U_df    = (0x7 << 23) | OPC_MSA_3R_12, +    OPC_ILVOD_df    = (0x7 << 23) | OPC_MSA_3R_14, +    OPC_HSUB_U_df   = (0x7 << 23) | OPC_MSA_3R_15, + +    /* ELM instructions df(bits 21..16) = _b, _h, _w, _d */ +    OPC_SLDI_df     = (0x0 << 22) | (0x00 << 16) | OPC_MSA_ELM, +    OPC_CTCMSA      = (0x0 << 22) | (0x3E << 16) | OPC_MSA_ELM, +    OPC_SPLATI_df   = (0x1 << 22) | (0x00 << 16) | OPC_MSA_ELM, +    OPC_CFCMSA      = (0x1 << 22) | (0x3E << 16) | OPC_MSA_ELM, +    OPC_COPY_S_df   = (0x2 << 22) | (0x00 << 16) | OPC_MSA_ELM, +    OPC_MOVE_V      = (0x2 << 22) | (0x3E << 16) | OPC_MSA_ELM, +    OPC_COPY_U_df   = (0x3 << 22) | (0x00 << 16) | OPC_MSA_ELM, +    OPC_INSERT_df   = (0x4 << 22) | (0x00 << 16) | OPC_MSA_ELM, +    OPC_INSVE_df    = (0x5 << 22) | (0x00 << 16) | OPC_MSA_ELM, + +    /* 3RF instruction _df(bit 21) = _w, _d */ +    OPC_FCAF_df     = (0x0 << 22) | OPC_MSA_3RF_1A, +    OPC_FADD_df     = (0x0 << 22) | OPC_MSA_3RF_1B, +    OPC_FCUN_df     = (0x1 << 22) | OPC_MSA_3RF_1A, +    OPC_FSUB_df     = (0x1 << 22) | OPC_MSA_3RF_1B, +    OPC_FCOR_df     = (0x1 << 22) | OPC_MSA_3RF_1C, +    OPC_FCEQ_df     = (0x2 << 22) | OPC_MSA_3RF_1A, +    OPC_FMUL_df     = (0x2 << 22) | OPC_MSA_3RF_1B, +    OPC_FCUNE_df    = (0x2 << 22) | OPC_MSA_3RF_1C, +    OPC_FCUEQ_df    = (0x3 << 22) | OPC_MSA_3RF_1A, +    OPC_FDIV_df     = (0x3 << 22) | OPC_MSA_3RF_1B, +    OPC_FCNE_df     = (0x3 << 22) | OPC_MSA_3RF_1C, +    OPC_FCLT_df     = (0x4 << 22) | OPC_MSA_3RF_1A, +    OPC_FMADD_df    = (0x4 << 22) | OPC_MSA_3RF_1B, +    OPC_MUL_Q_df    = (0x4 << 22) | OPC_MSA_3RF_1C, +    OPC_FCULT_df    = (0x5 << 22) | OPC_MSA_3RF_1A, +    OPC_FMSUB_df    = (0x5 << 22) | OPC_MSA_3RF_1B, +    OPC_MADD_Q_df   = (0x5 << 22) | OPC_MSA_3RF_1C, +    OPC_FCLE_df     = (0x6 << 22) | OPC_MSA_3RF_1A, +    OPC_MSUB_Q_df   = (0x6 << 22) | OPC_MSA_3RF_1C, +    OPC_FCULE_df    = (0x7 << 22) | OPC_MSA_3RF_1A, +    OPC_FEXP2_df    = (0x7 << 22) | OPC_MSA_3RF_1B, +    OPC_FSAF_df     = (0x8 << 22) | OPC_MSA_3RF_1A, +    OPC_FEXDO_df    = (0x8 << 22) | OPC_MSA_3RF_1B, +    OPC_FSUN_df     = (0x9 << 22) | OPC_MSA_3RF_1A, +    OPC_FSOR_df     = (0x9 << 22) | OPC_MSA_3RF_1C, +    OPC_FSEQ_df     = (0xA << 22) | OPC_MSA_3RF_1A, +    OPC_FTQ_df      = (0xA << 22) | OPC_MSA_3RF_1B, +    OPC_FSUNE_df    = (0xA << 22) | OPC_MSA_3RF_1C, +    OPC_FSUEQ_df    = (0xB << 22) | OPC_MSA_3RF_1A, +    OPC_FSNE_df     = (0xB << 22) | OPC_MSA_3RF_1C, +    OPC_FSLT_df     = (0xC << 22) | OPC_MSA_3RF_1A, +    OPC_FMIN_df     = (0xC << 22) | OPC_MSA_3RF_1B, +    OPC_MULR_Q_df   = (0xC << 22) | OPC_MSA_3RF_1C, +    OPC_FSULT_df    = (0xD << 22) | OPC_MSA_3RF_1A, +    OPC_FMIN_A_df   = (0xD << 22) | OPC_MSA_3RF_1B, +    OPC_MADDR_Q_df  = (0xD << 22) | OPC_MSA_3RF_1C, +    OPC_FSLE_df     = (0xE << 22) | OPC_MSA_3RF_1A, +    OPC_FMAX_df     = (0xE << 22) | OPC_MSA_3RF_1B, +    OPC_MSUBR_Q_df  = (0xE << 22) | OPC_MSA_3RF_1C, +    OPC_FSULE_df    = (0xF << 22) | OPC_MSA_3RF_1A, +    OPC_FMAX_A_df   = (0xF << 22) | OPC_MSA_3RF_1B, + +    /* BIT instruction df(bits 22..16) = _B _H _W _D */ +    OPC_SLLI_df     = (0x0 << 23) | OPC_MSA_BIT_09, +    OPC_SAT_S_df    = (0x0 << 23) | OPC_MSA_BIT_0A, +    OPC_SRAI_df     = (0x1 << 23) | OPC_MSA_BIT_09, +    OPC_SAT_U_df    = (0x1 << 23) | OPC_MSA_BIT_0A, +    OPC_SRLI_df     = (0x2 << 23) | OPC_MSA_BIT_09, +    OPC_SRARI_df    = (0x2 << 23) | OPC_MSA_BIT_0A, +    OPC_BCLRI_df    = (0x3 << 23) | OPC_MSA_BIT_09, +    OPC_SRLRI_df    = (0x3 << 23) | OPC_MSA_BIT_0A, +    OPC_BSETI_df    = (0x4 << 23) | OPC_MSA_BIT_09, +    OPC_BNEGI_df    = (0x5 << 23) | OPC_MSA_BIT_09, +    OPC_BINSLI_df   = (0x6 << 23) | OPC_MSA_BIT_09, +    OPC_BINSRI_df   = (0x7 << 23) | OPC_MSA_BIT_09, +}; + +/* global register indices */ +static TCGv_ptr cpu_env; +static TCGv cpu_gpr[32], cpu_PC; +static TCGv cpu_HI[MIPS_DSP_ACC], cpu_LO[MIPS_DSP_ACC]; +static TCGv cpu_dspctrl, btarget, bcond; +static TCGv_i32 hflags; +static TCGv_i32 fpu_fcr0, fpu_fcr31; +static TCGv_i64 fpu_f64[32]; +static TCGv_i64 msa_wr_d[64]; + +static uint32_t gen_opc_hflags[OPC_BUF_SIZE]; +static target_ulong gen_opc_btarget[OPC_BUF_SIZE]; + +#include "exec/gen-icount.h" + +#define gen_helper_0e0i(name, arg) do {                           \ +    TCGv_i32 helper_tmp = tcg_const_i32(arg);                     \ +    gen_helper_##name(cpu_env, helper_tmp);                       \ +    tcg_temp_free_i32(helper_tmp);                                \ +    } while(0) + +#define gen_helper_0e1i(name, arg1, arg2) do {                    \ +    TCGv_i32 helper_tmp = tcg_const_i32(arg2);                    \ +    gen_helper_##name(cpu_env, arg1, helper_tmp);                 \ +    tcg_temp_free_i32(helper_tmp);                                \ +    } while(0) + +#define gen_helper_1e0i(name, ret, arg1) do {                     \ +    TCGv_i32 helper_tmp = tcg_const_i32(arg1);                    \ +    gen_helper_##name(ret, cpu_env, helper_tmp);                  \ +    tcg_temp_free_i32(helper_tmp);                                \ +    } while(0) + +#define gen_helper_1e1i(name, ret, arg1, arg2) do {               \ +    TCGv_i32 helper_tmp = tcg_const_i32(arg2);                    \ +    gen_helper_##name(ret, cpu_env, arg1, helper_tmp);            \ +    tcg_temp_free_i32(helper_tmp);                                \ +    } while(0) + +#define gen_helper_0e2i(name, arg1, arg2, arg3) do {              \ +    TCGv_i32 helper_tmp = tcg_const_i32(arg3);                    \ +    gen_helper_##name(cpu_env, arg1, arg2, helper_tmp);           \ +    tcg_temp_free_i32(helper_tmp);                                \ +    } while(0) + +#define gen_helper_1e2i(name, ret, arg1, arg2, arg3) do {         \ +    TCGv_i32 helper_tmp = tcg_const_i32(arg3);                    \ +    gen_helper_##name(ret, cpu_env, arg1, arg2, helper_tmp);      \ +    tcg_temp_free_i32(helper_tmp);                                \ +    } while(0) + +#define gen_helper_0e3i(name, arg1, arg2, arg3, arg4) do {        \ +    TCGv_i32 helper_tmp = tcg_const_i32(arg4);                    \ +    gen_helper_##name(cpu_env, arg1, arg2, arg3, helper_tmp);     \ +    tcg_temp_free_i32(helper_tmp);                                \ +    } while(0) + +typedef struct DisasContext { +    struct TranslationBlock *tb; +    target_ulong pc, saved_pc; +    uint32_t opcode; +    int singlestep_enabled; +    int insn_flags; +    int32_t CP0_Config1; +    /* Routine used to access memory */ +    int mem_idx; +    TCGMemOp default_tcg_memop_mask; +    uint32_t hflags, saved_hflags; +    int bstate; +    target_ulong btarget; +    bool ulri; +    int kscrexist; +    bool rxi; +    int ie; +    bool bi; +    bool bp; +    uint64_t PAMask; +    bool mvh; +    int CP0_LLAddr_shift; +    bool ps; +} DisasContext; + +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 */ +}; + +static const char * const regnames[] = { +    "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", +    "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", +    "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", +    "t8", "t9", "k0", "k1", "gp", "sp", "s8", "ra", +}; + +static const char * const regnames_HI[] = { +    "HI0", "HI1", "HI2", "HI3", +}; + +static const char * const regnames_LO[] = { +    "LO0", "LO1", "LO2", "LO3", +}; + +static const char * const fregnames[] = { +    "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7", +    "f8",  "f9",  "f10", "f11", "f12", "f13", "f14", "f15", +    "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", +    "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", +}; + +static const char * const msaregnames[] = { +    "w0.d0",  "w0.d1",  "w1.d0",  "w1.d1", +    "w2.d0",  "w2.d1",  "w3.d0",  "w3.d1", +    "w4.d0",  "w4.d1",  "w5.d0",  "w5.d1", +    "w6.d0",  "w6.d1",  "w7.d0",  "w7.d1", +    "w8.d0",  "w8.d1",  "w9.d0",  "w9.d1", +    "w10.d0", "w10.d1", "w11.d0", "w11.d1", +    "w12.d0", "w12.d1", "w13.d0", "w13.d1", +    "w14.d0", "w14.d1", "w15.d0", "w15.d1", +    "w16.d0", "w16.d1", "w17.d0", "w17.d1", +    "w18.d0", "w18.d1", "w19.d0", "w19.d1", +    "w20.d0", "w20.d1", "w21.d0", "w21.d1", +    "w22.d0", "w22.d1", "w23.d0", "w23.d1", +    "w24.d0", "w24.d1", "w25.d0", "w25.d1", +    "w26.d0", "w26.d1", "w27.d0", "w27.d1", +    "w28.d0", "w28.d1", "w29.d0", "w29.d1", +    "w30.d0", "w30.d1", "w31.d0", "w31.d1", +}; + +#define MIPS_DEBUG(fmt, ...)                                                  \ +    do {                                                                      \ +        if (MIPS_DEBUG_DISAS) {                                               \ +            qemu_log_mask(CPU_LOG_TB_IN_ASM,                                  \ +                          TARGET_FMT_lx ": %08x " fmt "\n",                   \ +                          ctx->pc, ctx->opcode , ## __VA_ARGS__);             \ +        }                                                                     \ +    } while (0) + +#define LOG_DISAS(...)                                                        \ +    do {                                                                      \ +        if (MIPS_DEBUG_DISAS) {                                               \ +            qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__);                 \ +        }                                                                     \ +    } while (0) + +#define MIPS_INVAL(op)                                                        \ +    MIPS_DEBUG("Invalid %s %03x %03x %03x", op, ctx->opcode >> 26,            \ +               ctx->opcode & 0x3F, ((ctx->opcode >> 16) & 0x1F)) + +/* General purpose registers moves. */ +static inline void gen_load_gpr (TCGv t, int reg) +{ +    if (reg == 0) +        tcg_gen_movi_tl(t, 0); +    else +        tcg_gen_mov_tl(t, cpu_gpr[reg]); +} + +static inline void gen_store_gpr (TCGv t, int reg) +{ +    if (reg != 0) +        tcg_gen_mov_tl(cpu_gpr[reg], t); +} + +/* Moves to/from shadow registers. */ +static inline void gen_load_srsgpr (int from, int to) +{ +    TCGv t0 = tcg_temp_new(); + +    if (from == 0) +        tcg_gen_movi_tl(t0, 0); +    else { +        TCGv_i32 t2 = tcg_temp_new_i32(); +        TCGv_ptr addr = tcg_temp_new_ptr(); + +        tcg_gen_ld_i32(t2, cpu_env, offsetof(CPUMIPSState, CP0_SRSCtl)); +        tcg_gen_shri_i32(t2, t2, CP0SRSCtl_PSS); +        tcg_gen_andi_i32(t2, t2, 0xf); +        tcg_gen_muli_i32(t2, t2, sizeof(target_ulong) * 32); +        tcg_gen_ext_i32_ptr(addr, t2); +        tcg_gen_add_ptr(addr, cpu_env, addr); + +        tcg_gen_ld_tl(t0, addr, sizeof(target_ulong) * from); +        tcg_temp_free_ptr(addr); +        tcg_temp_free_i32(t2); +    } +    gen_store_gpr(t0, to); +    tcg_temp_free(t0); +} + +static inline void gen_store_srsgpr (int from, int to) +{ +    if (to != 0) { +        TCGv t0 = tcg_temp_new(); +        TCGv_i32 t2 = tcg_temp_new_i32(); +        TCGv_ptr addr = tcg_temp_new_ptr(); + +        gen_load_gpr(t0, from); +        tcg_gen_ld_i32(t2, cpu_env, offsetof(CPUMIPSState, CP0_SRSCtl)); +        tcg_gen_shri_i32(t2, t2, CP0SRSCtl_PSS); +        tcg_gen_andi_i32(t2, t2, 0xf); +        tcg_gen_muli_i32(t2, t2, sizeof(target_ulong) * 32); +        tcg_gen_ext_i32_ptr(addr, t2); +        tcg_gen_add_ptr(addr, cpu_env, addr); + +        tcg_gen_st_tl(t0, addr, sizeof(target_ulong) * to); +        tcg_temp_free_ptr(addr); +        tcg_temp_free_i32(t2); +        tcg_temp_free(t0); +    } +} + +/* Tests */ +static inline void gen_save_pc(target_ulong pc) +{ +    tcg_gen_movi_tl(cpu_PC, pc); +} + +static inline void save_cpu_state(DisasContext *ctx, int do_save_pc) +{ +    LOG_DISAS("hflags %08x saved %08x\n", ctx->hflags, ctx->saved_hflags); +    if (do_save_pc && ctx->pc != ctx->saved_pc) { +        gen_save_pc(ctx->pc); +        ctx->saved_pc = ctx->pc; +    } +    if (ctx->hflags != ctx->saved_hflags) { +        tcg_gen_movi_i32(hflags, ctx->hflags); +        ctx->saved_hflags = ctx->hflags; +        switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) { +        case MIPS_HFLAG_BR: +            break; +        case MIPS_HFLAG_BC: +        case MIPS_HFLAG_BL: +        case MIPS_HFLAG_B: +            tcg_gen_movi_tl(btarget, ctx->btarget); +            break; +        } +    } +} + +static inline void restore_cpu_state(CPUMIPSState *env, DisasContext *ctx) +{ +    ctx->saved_hflags = ctx->hflags; +    switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) { +    case MIPS_HFLAG_BR: +        break; +    case MIPS_HFLAG_BC: +    case MIPS_HFLAG_BL: +    case MIPS_HFLAG_B: +        ctx->btarget = env->btarget; +        break; +    } +} + +static inline void generate_exception_err(DisasContext *ctx, int excp, int err) +{ +    TCGv_i32 texcp = tcg_const_i32(excp); +    TCGv_i32 terr = tcg_const_i32(err); +    save_cpu_state(ctx, 1); +    gen_helper_raise_exception_err(cpu_env, texcp, terr); +    tcg_temp_free_i32(terr); +    tcg_temp_free_i32(texcp); +} + +static inline void generate_exception(DisasContext *ctx, int excp) +{ +    save_cpu_state(ctx, 1); +    gen_helper_0e0i(raise_exception, excp); +} + +/* Floating point register moves. */ +static void gen_load_fpr32(DisasContext *ctx, TCGv_i32 t, int reg) +{ +    if (ctx->hflags & MIPS_HFLAG_FRE) { +        generate_exception(ctx, EXCP_RI); +    } +    tcg_gen_trunc_i64_i32(t, fpu_f64[reg]); +} + +static void gen_store_fpr32(DisasContext *ctx, TCGv_i32 t, int reg) +{ +    TCGv_i64 t64; +    if (ctx->hflags & MIPS_HFLAG_FRE) { +        generate_exception(ctx, EXCP_RI); +    } +    t64 = tcg_temp_new_i64(); +    tcg_gen_extu_i32_i64(t64, t); +    tcg_gen_deposit_i64(fpu_f64[reg], fpu_f64[reg], t64, 0, 32); +    tcg_temp_free_i64(t64); +} + +static void gen_load_fpr32h(DisasContext *ctx, TCGv_i32 t, int reg) +{ +    if (ctx->hflags & MIPS_HFLAG_F64) { +        TCGv_i64 t64 = tcg_temp_new_i64(); +        tcg_gen_shri_i64(t64, fpu_f64[reg], 32); +        tcg_gen_trunc_i64_i32(t, t64); +        tcg_temp_free_i64(t64); +    } else { +        gen_load_fpr32(ctx, t, reg | 1); +    } +} + +static void gen_store_fpr32h(DisasContext *ctx, TCGv_i32 t, int reg) +{ +    if (ctx->hflags & MIPS_HFLAG_F64) { +        TCGv_i64 t64 = tcg_temp_new_i64(); +        tcg_gen_extu_i32_i64(t64, t); +        tcg_gen_deposit_i64(fpu_f64[reg], fpu_f64[reg], t64, 32, 32); +        tcg_temp_free_i64(t64); +    } else { +        gen_store_fpr32(ctx, t, reg | 1); +    } +} + +static void gen_load_fpr64(DisasContext *ctx, TCGv_i64 t, int reg) +{ +    if (ctx->hflags & MIPS_HFLAG_F64) { +        tcg_gen_mov_i64(t, fpu_f64[reg]); +    } else { +        tcg_gen_concat32_i64(t, fpu_f64[reg & ~1], fpu_f64[reg | 1]); +    } +} + +static void gen_store_fpr64(DisasContext *ctx, TCGv_i64 t, int reg) +{ +    if (ctx->hflags & MIPS_HFLAG_F64) { +        tcg_gen_mov_i64(fpu_f64[reg], t); +    } else { +        TCGv_i64 t0; +        tcg_gen_deposit_i64(fpu_f64[reg & ~1], fpu_f64[reg & ~1], t, 0, 32); +        t0 = tcg_temp_new_i64(); +        tcg_gen_shri_i64(t0, t, 32); +        tcg_gen_deposit_i64(fpu_f64[reg | 1], fpu_f64[reg | 1], t0, 0, 32); +        tcg_temp_free_i64(t0); +    } +} + +static inline int get_fp_bit (int cc) +{ +    if (cc) +        return 24 + cc; +    else +        return 23; +} + +/* Addresses computation */ +static inline void gen_op_addr_add (DisasContext *ctx, TCGv ret, TCGv arg0, TCGv arg1) +{ +    tcg_gen_add_tl(ret, arg0, arg1); + +#if defined(TARGET_MIPS64) +    if (ctx->hflags & MIPS_HFLAG_AWRAP) { +        tcg_gen_ext32s_i64(ret, ret); +    } +#endif +} + +/* Addresses computation (translation time) */ +static target_long addr_add(DisasContext *ctx, target_long base, +                            target_long offset) +{ +    target_long sum = base + offset; + +#if defined(TARGET_MIPS64) +    if (ctx->hflags & MIPS_HFLAG_AWRAP) { +        sum = (int32_t)sum; +    } +#endif +    return sum; +} + +static inline void gen_move_low32(TCGv ret, TCGv_i64 arg) +{ +#if defined(TARGET_MIPS64) +    tcg_gen_ext32s_tl(ret, arg); +#else +    tcg_gen_trunc_i64_tl(ret, arg); +#endif +} + +static inline void check_cp0_enabled(DisasContext *ctx) +{ +    if (unlikely(!(ctx->hflags & MIPS_HFLAG_CP0))) +        generate_exception_err(ctx, EXCP_CpU, 0); +} + +static inline void check_cp1_enabled(DisasContext *ctx) +{ +    if (unlikely(!(ctx->hflags & MIPS_HFLAG_FPU))) +        generate_exception_err(ctx, EXCP_CpU, 1); +} + +/* Verify that the processor is running with COP1X instructions enabled. +   This is associated with the nabla symbol in the MIPS32 and MIPS64 +   opcode tables.  */ + +static inline void check_cop1x(DisasContext *ctx) +{ +    if (unlikely(!(ctx->hflags & MIPS_HFLAG_COP1X))) +        generate_exception(ctx, EXCP_RI); +} + +/* Verify that the processor is running with 64-bit floating-point +   operations enabled.  */ + +static inline void check_cp1_64bitmode(DisasContext *ctx) +{ +    if (unlikely(~ctx->hflags & (MIPS_HFLAG_F64 | MIPS_HFLAG_COP1X))) +        generate_exception(ctx, EXCP_RI); +} + +/* + * Verify if floating point register is valid; an operation is not defined + * if bit 0 of any register specification is set and the FR bit in the + * Status register equals zero, since the register numbers specify an + * even-odd pair of adjacent coprocessor general registers. When the FR bit + * in the Status register equals one, both even and odd register numbers + * are valid. This limitation exists only for 64 bit wide (d,l,ps) registers. + * + * Multiple 64 bit wide registers can be checked by calling + * gen_op_cp1_registers(freg1 | freg2 | ... | fregN); + */ +static inline void check_cp1_registers(DisasContext *ctx, int regs) +{ +    if (unlikely(!(ctx->hflags & MIPS_HFLAG_F64) && (regs & 1))) +        generate_exception(ctx, EXCP_RI); +} + +/* Verify that the processor is running with DSP instructions enabled. +   This is enabled by CP0 Status register MX(24) bit. + */ + +static inline void check_dsp(DisasContext *ctx) +{ +    if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSP))) { +        if (ctx->insn_flags & ASE_DSP) { +            generate_exception(ctx, EXCP_DSPDIS); +        } else { +            generate_exception(ctx, EXCP_RI); +        } +    } +} + +static inline void check_dspr2(DisasContext *ctx) +{ +    if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSPR2))) { +        if (ctx->insn_flags & ASE_DSP) { +            generate_exception(ctx, EXCP_DSPDIS); +        } else { +            generate_exception(ctx, EXCP_RI); +        } +    } +} + +/* This code generates a "reserved instruction" exception if the +   CPU does not support the instruction set corresponding to flags. */ +static inline void check_insn(DisasContext *ctx, int flags) +{ +    if (unlikely(!(ctx->insn_flags & flags))) { +        generate_exception(ctx, EXCP_RI); +    } +} + +/* This code generates a "reserved instruction" exception if the +   CPU has corresponding flag set which indicates that the instruction +   has been removed. */ +static inline void check_insn_opc_removed(DisasContext *ctx, int flags) +{ +    if (unlikely(ctx->insn_flags & flags)) { +        generate_exception(ctx, EXCP_RI); +    } +} + +/* This code generates a "reserved instruction" exception if the +   CPU does not support 64-bit paired-single (PS) floating point data type */ +static inline void check_ps(DisasContext *ctx) +{ +    if (unlikely(!ctx->ps)) { +        generate_exception(ctx, EXCP_RI); +    } +    check_cp1_64bitmode(ctx); +} + +#ifdef TARGET_MIPS64 +/* This code generates a "reserved instruction" exception if 64-bit +   instructions are not enabled. */ +static inline void check_mips_64(DisasContext *ctx) +{ +    if (unlikely(!(ctx->hflags & MIPS_HFLAG_64))) +        generate_exception(ctx, EXCP_RI); +} +#endif + +#ifndef CONFIG_USER_ONLY +static inline void check_mvh(DisasContext *ctx) +{ +    if (unlikely(!ctx->mvh)) { +        generate_exception(ctx, EXCP_RI); +    } +} +#endif + +/* Define small wrappers for gen_load_fpr* so that we have a uniform +   calling interface for 32 and 64-bit FPRs.  No sense in changing +   all callers for gen_load_fpr32 when we need the CTX parameter for +   this one use.  */ +#define gen_ldcmp_fpr32(ctx, x, y) gen_load_fpr32(ctx, x, y) +#define gen_ldcmp_fpr64(ctx, x, y) gen_load_fpr64(ctx, x, y) +#define FOP_CONDS(type, abs, fmt, ifmt, bits)                                 \ +static inline void gen_cmp ## type ## _ ## fmt(DisasContext *ctx, int n,      \ +                                               int ft, int fs, int cc)        \ +{                                                                             \ +    TCGv_i##bits fp0 = tcg_temp_new_i##bits ();                               \ +    TCGv_i##bits fp1 = tcg_temp_new_i##bits ();                               \ +    switch (ifmt) {                                                           \ +    case FMT_PS:                                                              \ +        check_ps(ctx);                                                        \ +        break;                                                                \ +    case FMT_D:                                                               \ +        if (abs) {                                                            \ +            check_cop1x(ctx);                                                 \ +        }                                                                     \ +        check_cp1_registers(ctx, fs | ft);                                    \ +        break;                                                                \ +    case FMT_S:                                                               \ +        if (abs) {                                                            \ +            check_cop1x(ctx);                                                 \ +        }                                                                     \ +        break;                                                                \ +    }                                                                         \ +    gen_ldcmp_fpr##bits (ctx, fp0, fs);                                       \ +    gen_ldcmp_fpr##bits (ctx, fp1, ft);                                       \ +    switch (n) {                                                              \ +    case  0: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _f, fp0, fp1, cc);    break;\ +    case  1: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _un, fp0, fp1, cc);   break;\ +    case  2: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _eq, fp0, fp1, cc);   break;\ +    case  3: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _ueq, fp0, fp1, cc);  break;\ +    case  4: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _olt, fp0, fp1, cc);  break;\ +    case  5: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _ult, fp0, fp1, cc);  break;\ +    case  6: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _ole, fp0, fp1, cc);  break;\ +    case  7: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _ule, fp0, fp1, cc);  break;\ +    case  8: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _sf, fp0, fp1, cc);   break;\ +    case  9: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _ngle, fp0, fp1, cc); break;\ +    case 10: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _seq, fp0, fp1, cc);  break;\ +    case 11: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _ngl, fp0, fp1, cc);  break;\ +    case 12: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _lt, fp0, fp1, cc);   break;\ +    case 13: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _nge, fp0, fp1, cc);  break;\ +    case 14: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _le, fp0, fp1, cc);   break;\ +    case 15: gen_helper_0e2i(cmp ## type ## _ ## fmt ## _ngt, fp0, fp1, cc);  break;\ +    default: abort();                                                         \ +    }                                                                         \ +    tcg_temp_free_i##bits (fp0);                                              \ +    tcg_temp_free_i##bits (fp1);                                              \ +} + +FOP_CONDS(, 0, d, FMT_D, 64) +FOP_CONDS(abs, 1, d, FMT_D, 64) +FOP_CONDS(, 0, s, FMT_S, 32) +FOP_CONDS(abs, 1, s, FMT_S, 32) +FOP_CONDS(, 0, ps, FMT_PS, 64) +FOP_CONDS(abs, 1, ps, FMT_PS, 64) +#undef FOP_CONDS + +#define FOP_CONDNS(fmt, ifmt, bits, STORE)                              \ +static inline void gen_r6_cmp_ ## fmt(DisasContext * ctx, int n,        \ +                                      int ft, int fs, int fd)           \ +{                                                                       \ +    TCGv_i ## bits fp0 = tcg_temp_new_i ## bits();                      \ +    TCGv_i ## bits fp1 = tcg_temp_new_i ## bits();                      \ +    if (ifmt == FMT_D) {                                                \ +        check_cp1_registers(ctx, fs | ft | fd);                         \ +    }                                                                   \ +    gen_ldcmp_fpr ## bits(ctx, fp0, fs);                                \ +    gen_ldcmp_fpr ## bits(ctx, fp1, ft);                                \ +    switch (n) {                                                        \ +    case  0:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _af(fp0, cpu_env, fp0, fp1);       \ +        break;                                                          \ +    case  1:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _un(fp0, cpu_env, fp0, fp1);       \ +        break;                                                          \ +    case  2:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _eq(fp0, cpu_env, fp0, fp1);       \ +        break;                                                          \ +    case  3:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _ueq(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    case  4:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _lt(fp0, cpu_env, fp0, fp1);       \ +        break;                                                          \ +    case  5:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _ult(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    case  6:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _le(fp0, cpu_env, fp0, fp1);       \ +        break;                                                          \ +    case  7:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _ule(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    case  8:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _saf(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    case  9:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _sun(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    case 10:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _seq(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    case 11:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _sueq(fp0, cpu_env, fp0, fp1);     \ +        break;                                                          \ +    case 12:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _slt(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    case 13:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _sult(fp0, cpu_env, fp0, fp1);     \ +        break;                                                          \ +    case 14:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _sle(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    case 15:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _sule(fp0, cpu_env, fp0, fp1);     \ +        break;                                                          \ +    case 17:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _or(fp0, cpu_env, fp0, fp1);       \ +        break;                                                          \ +    case 18:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _une(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    case 19:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _ne(fp0, cpu_env, fp0, fp1);       \ +        break;                                                          \ +    case 25:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _sor(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    case 26:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _sune(fp0, cpu_env, fp0, fp1);     \ +        break;                                                          \ +    case 27:                                                            \ +        gen_helper_r6_cmp_ ## fmt ## _sne(fp0, cpu_env, fp0, fp1);      \ +        break;                                                          \ +    default:                                                            \ +        abort();                                                        \ +    }                                                                   \ +    STORE;                                                              \ +    tcg_temp_free_i ## bits (fp0);                                      \ +    tcg_temp_free_i ## bits (fp1);                                      \ +} + +FOP_CONDNS(d, FMT_D, 64, gen_store_fpr64(ctx, fp0, fd)) +FOP_CONDNS(s, FMT_S, 32, gen_store_fpr32(ctx, fp0, fd)) +#undef FOP_CONDNS +#undef gen_ldcmp_fpr32 +#undef gen_ldcmp_fpr64 + +/* load/store instructions. */ +#ifdef CONFIG_USER_ONLY +#define OP_LD_ATOMIC(insn,fname)                                           \ +static inline void op_ld_##insn(TCGv ret, TCGv arg1, DisasContext *ctx)    \ +{                                                                          \ +    TCGv t0 = tcg_temp_new();                                              \ +    tcg_gen_mov_tl(t0, arg1);                                              \ +    tcg_gen_qemu_##fname(ret, arg1, ctx->mem_idx);                         \ +    tcg_gen_st_tl(t0, cpu_env, offsetof(CPUMIPSState, lladdr));                \ +    tcg_gen_st_tl(ret, cpu_env, offsetof(CPUMIPSState, llval));                \ +    tcg_temp_free(t0);                                                     \ +} +#else +#define OP_LD_ATOMIC(insn,fname)                                           \ +static inline void op_ld_##insn(TCGv ret, TCGv arg1, DisasContext *ctx)    \ +{                                                                          \ +    gen_helper_1e1i(insn, ret, arg1, ctx->mem_idx);                        \ +} +#endif +OP_LD_ATOMIC(ll,ld32s); +#if defined(TARGET_MIPS64) +OP_LD_ATOMIC(lld,ld64); +#endif +#undef OP_LD_ATOMIC + +#ifdef CONFIG_USER_ONLY +#define OP_ST_ATOMIC(insn,fname,ldname,almask)                               \ +static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \ +{                                                                            \ +    TCGv t0 = tcg_temp_new();                                                \ +    TCGLabel *l1 = gen_new_label();                                          \ +    TCGLabel *l2 = gen_new_label();                                          \ +                                                                             \ +    tcg_gen_andi_tl(t0, arg2, almask);                                       \ +    tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1);                              \ +    tcg_gen_st_tl(arg2, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr));          \ +    generate_exception(ctx, EXCP_AdES);                                      \ +    gen_set_label(l1);                                                       \ +    tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUMIPSState, lladdr));                  \ +    tcg_gen_brcond_tl(TCG_COND_NE, arg2, t0, l2);                            \ +    tcg_gen_movi_tl(t0, rt | ((almask << 3) & 0x20));                        \ +    tcg_gen_st_tl(t0, cpu_env, offsetof(CPUMIPSState, llreg));                   \ +    tcg_gen_st_tl(arg1, cpu_env, offsetof(CPUMIPSState, llnewval));              \ +    gen_helper_0e0i(raise_exception, EXCP_SC);                               \ +    gen_set_label(l2);                                                       \ +    tcg_gen_movi_tl(t0, 0);                                                  \ +    gen_store_gpr(t0, rt);                                                   \ +    tcg_temp_free(t0);                                                       \ +} +#else +#define OP_ST_ATOMIC(insn,fname,ldname,almask)                               \ +static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, DisasContext *ctx) \ +{                                                                            \ +    TCGv t0 = tcg_temp_new();                                                \ +    gen_helper_1e2i(insn, t0, arg1, arg2, ctx->mem_idx);                     \ +    gen_store_gpr(t0, rt);                                                   \ +    tcg_temp_free(t0);                                                       \ +} +#endif +OP_ST_ATOMIC(sc,st32,ld32s,0x3); +#if defined(TARGET_MIPS64) +OP_ST_ATOMIC(scd,st64,ld64,0x7); +#endif +#undef OP_ST_ATOMIC + +static void gen_base_offset_addr (DisasContext *ctx, TCGv addr, +                                  int base, int16_t offset) +{ +    if (base == 0) { +        tcg_gen_movi_tl(addr, offset); +    } else if (offset == 0) { +        gen_load_gpr(addr, base); +    } else { +        tcg_gen_movi_tl(addr, offset); +        gen_op_addr_add(ctx, addr, cpu_gpr[base], addr); +    } +} + +static target_ulong pc_relative_pc (DisasContext *ctx) +{ +    target_ulong pc = ctx->pc; + +    if (ctx->hflags & MIPS_HFLAG_BMASK) { +        int branch_bytes = ctx->hflags & MIPS_HFLAG_BDS16 ? 2 : 4; + +        pc -= branch_bytes; +    } + +    pc &= ~(target_ulong)3; +    return pc; +} + +/* Load */ +static void gen_ld(DisasContext *ctx, uint32_t opc, +                   int rt, int base, int16_t offset) +{ +    const char *opn = "ld"; +    TCGv t0, t1, t2; + +    if (rt == 0 && ctx->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)) { +        /* Loongson CPU uses a load to zero register for prefetch. +           We emulate it as a NOP. On other CPU we must perform the +           actual memory access. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    gen_base_offset_addr(ctx, t0, base, offset); + +    switch (opc) { +#if defined(TARGET_MIPS64) +    case OPC_LWU: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL | +                           ctx->default_tcg_memop_mask); +        gen_store_gpr(t0, rt); +        opn = "lwu"; +        break; +    case OPC_LD: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ | +                           ctx->default_tcg_memop_mask); +        gen_store_gpr(t0, rt); +        opn = "ld"; +        break; +    case OPC_LLD: +    case R6_OPC_LLD: +        save_cpu_state(ctx, 1); +        op_ld_lld(t0, t0, ctx); +        gen_store_gpr(t0, rt); +        opn = "lld"; +        break; +    case OPC_LDL: +        t1 = tcg_temp_new(); +        /* Do a byte access to possibly trigger a page +           fault with the unaligned address.  */ +        tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); +        tcg_gen_andi_tl(t1, t0, 7); +#ifndef TARGET_WORDS_BIGENDIAN +        tcg_gen_xori_tl(t1, t1, 7); +#endif +        tcg_gen_shli_tl(t1, t1, 3); +        tcg_gen_andi_tl(t0, t0, ~7); +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ); +        tcg_gen_shl_tl(t0, t0, t1); +        tcg_gen_xori_tl(t1, t1, 63); +        t2 = tcg_const_tl(0x7fffffffffffffffull); +        tcg_gen_shr_tl(t2, t2, t1); +        gen_load_gpr(t1, rt); +        tcg_gen_and_tl(t1, t1, t2); +        tcg_temp_free(t2); +        tcg_gen_or_tl(t0, t0, t1); +        tcg_temp_free(t1); +        gen_store_gpr(t0, rt); +        opn = "ldl"; +        break; +    case OPC_LDR: +        t1 = tcg_temp_new(); +        /* Do a byte access to possibly trigger a page +           fault with the unaligned address.  */ +        tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); +        tcg_gen_andi_tl(t1, t0, 7); +#ifdef TARGET_WORDS_BIGENDIAN +        tcg_gen_xori_tl(t1, t1, 7); +#endif +        tcg_gen_shli_tl(t1, t1, 3); +        tcg_gen_andi_tl(t0, t0, ~7); +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ); +        tcg_gen_shr_tl(t0, t0, t1); +        tcg_gen_xori_tl(t1, t1, 63); +        t2 = tcg_const_tl(0xfffffffffffffffeull); +        tcg_gen_shl_tl(t2, t2, t1); +        gen_load_gpr(t1, rt); +        tcg_gen_and_tl(t1, t1, t2); +        tcg_temp_free(t2); +        tcg_gen_or_tl(t0, t0, t1); +        tcg_temp_free(t1); +        gen_store_gpr(t0, rt); +        opn = "ldr"; +        break; +    case OPC_LDPC: +        t1 = tcg_const_tl(pc_relative_pc(ctx)); +        gen_op_addr_add(ctx, t0, t0, t1); +        tcg_temp_free(t1); +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ); +        gen_store_gpr(t0, rt); +        opn = "ldpc"; +        break; +#endif +    case OPC_LWPC: +        t1 = tcg_const_tl(pc_relative_pc(ctx)); +        gen_op_addr_add(ctx, t0, t0, t1); +        tcg_temp_free(t1); +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL); +        gen_store_gpr(t0, rt); +        opn = "lwpc"; +        break; +    case OPC_LW: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL | +                           ctx->default_tcg_memop_mask); +        gen_store_gpr(t0, rt); +        opn = "lw"; +        break; +    case OPC_LH: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW | +                           ctx->default_tcg_memop_mask); +        gen_store_gpr(t0, rt); +        opn = "lh"; +        break; +    case OPC_LHU: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUW | +                           ctx->default_tcg_memop_mask); +        gen_store_gpr(t0, rt); +        opn = "lhu"; +        break; +    case OPC_LB: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_SB); +        gen_store_gpr(t0, rt); +        opn = "lb"; +        break; +    case OPC_LBU: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_UB); +        gen_store_gpr(t0, rt); +        opn = "lbu"; +        break; +    case OPC_LWL: +        t1 = tcg_temp_new(); +        /* Do a byte access to possibly trigger a page +           fault with the unaligned address.  */ +        tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); +        tcg_gen_andi_tl(t1, t0, 3); +#ifndef TARGET_WORDS_BIGENDIAN +        tcg_gen_xori_tl(t1, t1, 3); +#endif +        tcg_gen_shli_tl(t1, t1, 3); +        tcg_gen_andi_tl(t0, t0, ~3); +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL); +        tcg_gen_shl_tl(t0, t0, t1); +        tcg_gen_xori_tl(t1, t1, 31); +        t2 = tcg_const_tl(0x7fffffffull); +        tcg_gen_shr_tl(t2, t2, t1); +        gen_load_gpr(t1, rt); +        tcg_gen_and_tl(t1, t1, t2); +        tcg_temp_free(t2); +        tcg_gen_or_tl(t0, t0, t1); +        tcg_temp_free(t1); +        tcg_gen_ext32s_tl(t0, t0); +        gen_store_gpr(t0, rt); +        opn = "lwl"; +        break; +    case OPC_LWR: +        t1 = tcg_temp_new(); +        /* Do a byte access to possibly trigger a page +           fault with the unaligned address.  */ +        tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); +        tcg_gen_andi_tl(t1, t0, 3); +#ifdef TARGET_WORDS_BIGENDIAN +        tcg_gen_xori_tl(t1, t1, 3); +#endif +        tcg_gen_shli_tl(t1, t1, 3); +        tcg_gen_andi_tl(t0, t0, ~3); +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEUL); +        tcg_gen_shr_tl(t0, t0, t1); +        tcg_gen_xori_tl(t1, t1, 31); +        t2 = tcg_const_tl(0xfffffffeull); +        tcg_gen_shl_tl(t2, t2, t1); +        gen_load_gpr(t1, rt); +        tcg_gen_and_tl(t1, t1, t2); +        tcg_temp_free(t2); +        tcg_gen_or_tl(t0, t0, t1); +        tcg_temp_free(t1); +        tcg_gen_ext32s_tl(t0, t0); +        gen_store_gpr(t0, rt); +        opn = "lwr"; +        break; +    case OPC_LL: +    case R6_OPC_LL: +        save_cpu_state(ctx, 1); +        op_ld_ll(t0, t0, ctx); +        gen_store_gpr(t0, rt); +        opn = "ll"; +        break; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]); +    tcg_temp_free(t0); +} + +/* Store */ +static void gen_st (DisasContext *ctx, uint32_t opc, int rt, +                    int base, int16_t offset) +{ +    const char *opn = "st"; +    TCGv t0 = tcg_temp_new(); +    TCGv t1 = tcg_temp_new(); + +    gen_base_offset_addr(ctx, t0, base, offset); +    gen_load_gpr(t1, rt); +    switch (opc) { +#if defined(TARGET_MIPS64) +    case OPC_SD: +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ | +                           ctx->default_tcg_memop_mask); +        opn = "sd"; +        break; +    case OPC_SDL: +        save_cpu_state(ctx, 1); +        gen_helper_0e2i(sdl, t1, t0, ctx->mem_idx); +        opn = "sdl"; +        break; +    case OPC_SDR: +        save_cpu_state(ctx, 1); +        gen_helper_0e2i(sdr, t1, t0, ctx->mem_idx); +        opn = "sdr"; +        break; +#endif +    case OPC_SW: +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL | +                           ctx->default_tcg_memop_mask); +        opn = "sw"; +        break; +    case OPC_SH: +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW | +                           ctx->default_tcg_memop_mask); +        opn = "sh"; +        break; +    case OPC_SB: +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_8); +        opn = "sb"; +        break; +    case OPC_SWL: +        save_cpu_state(ctx, 1); +        gen_helper_0e2i(swl, t1, t0, ctx->mem_idx); +        opn = "swl"; +        break; +    case OPC_SWR: +        save_cpu_state(ctx, 1); +        gen_helper_0e2i(swr, t1, t0, ctx->mem_idx); +        opn = "swr"; +        break; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]); +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + + +/* Store conditional */ +static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, +                         int base, int16_t offset) +{ +    const char *opn = "st_cond"; +    TCGv t0, t1; + +#ifdef CONFIG_USER_ONLY +    t0 = tcg_temp_local_new(); +    t1 = tcg_temp_local_new(); +#else +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); +#endif +    gen_base_offset_addr(ctx, t0, base, offset); +    gen_load_gpr(t1, rt); +    switch (opc) { +#if defined(TARGET_MIPS64) +    case OPC_SCD: +    case R6_OPC_SCD: +        save_cpu_state(ctx, 1); +        op_st_scd(t1, t0, rt, ctx); +        opn = "scd"; +        break; +#endif +    case OPC_SC: +    case R6_OPC_SC: +        save_cpu_state(ctx, 1); +        op_st_sc(t1, t0, rt, ctx); +        opn = "sc"; +        break; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %d(%s)", opn, regnames[rt], offset, regnames[base]); +    tcg_temp_free(t1); +    tcg_temp_free(t0); +} + +/* Load and store */ +static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, +                          int base, int16_t offset) +{ +    const char *opn = "flt_ldst"; +    TCGv t0 = tcg_temp_new(); + +    gen_base_offset_addr(ctx, t0, base, offset); +    /* Don't do NOP if destination is zero: we must perform the actual +       memory access. */ +    switch (opc) { +    case OPC_LWC1: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            tcg_gen_qemu_ld_i32(fp0, t0, ctx->mem_idx, MO_TESL | +                                ctx->default_tcg_memop_mask); +            gen_store_fpr32(ctx, fp0, ft); +            tcg_temp_free_i32(fp0); +        } +        opn = "lwc1"; +        break; +    case OPC_SWC1: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, ft); +            tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL | +                                ctx->default_tcg_memop_mask); +            tcg_temp_free_i32(fp0); +        } +        opn = "swc1"; +        break; +    case OPC_LDC1: +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEQ | +                                ctx->default_tcg_memop_mask); +            gen_store_fpr64(ctx, fp0, ft); +            tcg_temp_free_i64(fp0); +        } +        opn = "ldc1"; +        break; +    case OPC_SDC1: +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, ft); +            tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ | +                                ctx->default_tcg_memop_mask); +            tcg_temp_free_i64(fp0); +        } +        opn = "sdc1"; +        break; +    default: +        MIPS_INVAL(opn); +        generate_exception(ctx, EXCP_RI); +        goto out; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %d(%s)", opn, fregnames[ft], offset, regnames[base]); + out: +    tcg_temp_free(t0); +} + +static void gen_cop1_ldst(DisasContext *ctx, uint32_t op, int rt, +                          int rs, int16_t imm) +{ +    if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { +        check_cp1_enabled(ctx); +        switch (op) { +        case OPC_LDC1: +        case OPC_SDC1: +            check_insn(ctx, ISA_MIPS2); +            /* Fallthrough */ +        default: +            gen_flt_ldst(ctx, op, rt, rs, imm); +        } +    } else { +        generate_exception_err(ctx, EXCP_CpU, 1); +    } +} + +/* Arithmetic with immediate operand */ +static void gen_arith_imm(DisasContext *ctx, uint32_t opc, +                          int rt, int rs, int16_t imm) +{ +    target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */ +    const char *opn = "imm arith"; + +    if (rt == 0 && opc != OPC_ADDI && opc != OPC_DADDI) { +        /* If no destination, treat it as a NOP. +           For addi, we must generate the overflow exception when needed. */ +        MIPS_DEBUG("NOP"); +        return; +    } +    switch (opc) { +    case OPC_ADDI: +        { +            TCGv t0 = tcg_temp_local_new(); +            TCGv t1 = tcg_temp_new(); +            TCGv t2 = tcg_temp_new(); +            TCGLabel *l1 = gen_new_label(); + +            gen_load_gpr(t1, rs); +            tcg_gen_addi_tl(t0, t1, uimm); +            tcg_gen_ext32s_tl(t0, t0); + +            tcg_gen_xori_tl(t1, t1, ~uimm); +            tcg_gen_xori_tl(t2, t0, uimm); +            tcg_gen_and_tl(t1, t1, t2); +            tcg_temp_free(t2); +            tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); +            tcg_temp_free(t1); +            /* operands of same sign, result different sign */ +            generate_exception(ctx, EXCP_OVERFLOW); +            gen_set_label(l1); +            tcg_gen_ext32s_tl(t0, t0); +            gen_store_gpr(t0, rt); +            tcg_temp_free(t0); +        } +        opn = "addi"; +        break; +    case OPC_ADDIU: +        if (rs != 0) { +            tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], uimm); +            tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rt], uimm); +        } +        opn = "addiu"; +        break; +#if defined(TARGET_MIPS64) +    case OPC_DADDI: +        { +            TCGv t0 = tcg_temp_local_new(); +            TCGv t1 = tcg_temp_new(); +            TCGv t2 = tcg_temp_new(); +            TCGLabel *l1 = gen_new_label(); + +            gen_load_gpr(t1, rs); +            tcg_gen_addi_tl(t0, t1, uimm); + +            tcg_gen_xori_tl(t1, t1, ~uimm); +            tcg_gen_xori_tl(t2, t0, uimm); +            tcg_gen_and_tl(t1, t1, t2); +            tcg_temp_free(t2); +            tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); +            tcg_temp_free(t1); +            /* operands of same sign, result different sign */ +            generate_exception(ctx, EXCP_OVERFLOW); +            gen_set_label(l1); +            gen_store_gpr(t0, rt); +            tcg_temp_free(t0); +        } +        opn = "daddi"; +        break; +    case OPC_DADDIU: +        if (rs != 0) { +            tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], uimm); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rt], uimm); +        } +        opn = "daddiu"; +        break; +#endif +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm); +} + +/* Logic with immediate operand */ +static void gen_logic_imm(DisasContext *ctx, uint32_t opc, +                          int rt, int rs, int16_t imm) +{ +    target_ulong uimm; + +    if (rt == 0) { +        /* If no destination, treat it as a NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } +    uimm = (uint16_t)imm; +    switch (opc) { +    case OPC_ANDI: +        if (likely(rs != 0)) +            tcg_gen_andi_tl(cpu_gpr[rt], cpu_gpr[rs], uimm); +        else +            tcg_gen_movi_tl(cpu_gpr[rt], 0); +        MIPS_DEBUG("andi %s, %s, " TARGET_FMT_lx, regnames[rt], +                   regnames[rs], uimm); +        break; +    case OPC_ORI: +        if (rs != 0) +            tcg_gen_ori_tl(cpu_gpr[rt], cpu_gpr[rs], uimm); +        else +            tcg_gen_movi_tl(cpu_gpr[rt], uimm); +        MIPS_DEBUG("ori %s, %s, " TARGET_FMT_lx, regnames[rt], +                   regnames[rs], uimm); +        break; +    case OPC_XORI: +        if (likely(rs != 0)) +            tcg_gen_xori_tl(cpu_gpr[rt], cpu_gpr[rs], uimm); +        else +            tcg_gen_movi_tl(cpu_gpr[rt], uimm); +        MIPS_DEBUG("xori %s, %s, " TARGET_FMT_lx, regnames[rt], +                   regnames[rs], uimm); +        break; +    case OPC_LUI: +        if (rs != 0 && (ctx->insn_flags & ISA_MIPS32R6)) { +            /* OPC_AUI */ +            tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], imm << 16); +            tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); +            MIPS_DEBUG("aui %s, %s, %04x", regnames[rt], regnames[rs], imm); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rt], imm << 16); +            MIPS_DEBUG("lui %s, " TARGET_FMT_lx, regnames[rt], uimm); +        } +        break; + +    default: +        MIPS_DEBUG("Unknown logical immediate opcode %08x", opc); +        break; +    } +} + +/* Set on less than with immediate operand */ +static void gen_slt_imm(DisasContext *ctx, uint32_t opc, +                        int rt, int rs, int16_t imm) +{ +    target_ulong uimm = (target_long)imm; /* Sign extend to 32/64 bits */ +    const char *opn = "imm arith"; +    TCGv t0; + +    if (rt == 0) { +        /* If no destination, treat it as a NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } +    t0 = tcg_temp_new(); +    gen_load_gpr(t0, rs); +    switch (opc) { +    case OPC_SLTI: +        tcg_gen_setcondi_tl(TCG_COND_LT, cpu_gpr[rt], t0, uimm); +        opn = "slti"; +        break; +    case OPC_SLTIU: +        tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_gpr[rt], t0, uimm); +        opn = "sltiu"; +        break; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm); +    tcg_temp_free(t0); +} + +/* Shifts with immediate operand */ +static void gen_shift_imm(DisasContext *ctx, uint32_t opc, +                          int rt, int rs, int16_t imm) +{ +    target_ulong uimm = ((uint16_t)imm) & 0x1f; +    const char *opn = "imm shift"; +    TCGv t0; + +    if (rt == 0) { +        /* If no destination, treat it as a NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    gen_load_gpr(t0, rs); +    switch (opc) { +    case OPC_SLL: +        tcg_gen_shli_tl(t0, t0, uimm); +        tcg_gen_ext32s_tl(cpu_gpr[rt], t0); +        opn = "sll"; +        break; +    case OPC_SRA: +        tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm); +        opn = "sra"; +        break; +    case OPC_SRL: +        if (uimm != 0) { +            tcg_gen_ext32u_tl(t0, t0); +            tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm); +        } else { +            tcg_gen_ext32s_tl(cpu_gpr[rt], t0); +        } +        opn = "srl"; +        break; +    case OPC_ROTR: +        if (uimm != 0) { +            TCGv_i32 t1 = tcg_temp_new_i32(); + +            tcg_gen_trunc_tl_i32(t1, t0); +            tcg_gen_rotri_i32(t1, t1, uimm); +            tcg_gen_ext_i32_tl(cpu_gpr[rt], t1); +            tcg_temp_free_i32(t1); +        } else { +            tcg_gen_ext32s_tl(cpu_gpr[rt], t0); +        } +        opn = "rotr"; +        break; +#if defined(TARGET_MIPS64) +    case OPC_DSLL: +        tcg_gen_shli_tl(cpu_gpr[rt], t0, uimm); +        opn = "dsll"; +        break; +    case OPC_DSRA: +        tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm); +        opn = "dsra"; +        break; +    case OPC_DSRL: +        tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm); +        opn = "dsrl"; +        break; +    case OPC_DROTR: +        if (uimm != 0) { +            tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm); +        } else { +            tcg_gen_mov_tl(cpu_gpr[rt], t0); +        } +        opn = "drotr"; +        break; +    case OPC_DSLL32: +        tcg_gen_shli_tl(cpu_gpr[rt], t0, uimm + 32); +        opn = "dsll32"; +        break; +    case OPC_DSRA32: +        tcg_gen_sari_tl(cpu_gpr[rt], t0, uimm + 32); +        opn = "dsra32"; +        break; +    case OPC_DSRL32: +        tcg_gen_shri_tl(cpu_gpr[rt], t0, uimm + 32); +        opn = "dsrl32"; +        break; +    case OPC_DROTR32: +        tcg_gen_rotri_tl(cpu_gpr[rt], t0, uimm + 32); +        opn = "drotr32"; +        break; +#endif +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, " TARGET_FMT_lx, opn, regnames[rt], regnames[rs], uimm); +    tcg_temp_free(t0); +} + +/* Arithmetic */ +static void gen_arith(DisasContext *ctx, uint32_t opc, +                      int rd, int rs, int rt) +{ +    const char *opn = "arith"; + +    if (rd == 0 && opc != OPC_ADD && opc != OPC_SUB +       && opc != OPC_DADD && opc != OPC_DSUB) { +        /* If no destination, treat it as a NOP. +           For add & sub, we must generate the overflow exception when needed. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    switch (opc) { +    case OPC_ADD: +        { +            TCGv t0 = tcg_temp_local_new(); +            TCGv t1 = tcg_temp_new(); +            TCGv t2 = tcg_temp_new(); +            TCGLabel *l1 = gen_new_label(); + +            gen_load_gpr(t1, rs); +            gen_load_gpr(t2, rt); +            tcg_gen_add_tl(t0, t1, t2); +            tcg_gen_ext32s_tl(t0, t0); +            tcg_gen_xor_tl(t1, t1, t2); +            tcg_gen_xor_tl(t2, t0, t2); +            tcg_gen_andc_tl(t1, t2, t1); +            tcg_temp_free(t2); +            tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); +            tcg_temp_free(t1); +            /* operands of same sign, result different sign */ +            generate_exception(ctx, EXCP_OVERFLOW); +            gen_set_label(l1); +            gen_store_gpr(t0, rd); +            tcg_temp_free(t0); +        } +        opn = "add"; +        break; +    case OPC_ADDU: +        if (rs != 0 && rt != 0) { +            tcg_gen_add_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +        } else if (rs == 0 && rt != 0) { +            tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rt]); +        } else if (rs != 0 && rt == 0) { +            tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +        } +        opn = "addu"; +        break; +    case OPC_SUB: +        { +            TCGv t0 = tcg_temp_local_new(); +            TCGv t1 = tcg_temp_new(); +            TCGv t2 = tcg_temp_new(); +            TCGLabel *l1 = gen_new_label(); + +            gen_load_gpr(t1, rs); +            gen_load_gpr(t2, rt); +            tcg_gen_sub_tl(t0, t1, t2); +            tcg_gen_ext32s_tl(t0, t0); +            tcg_gen_xor_tl(t2, t1, t2); +            tcg_gen_xor_tl(t1, t0, t1); +            tcg_gen_and_tl(t1, t1, t2); +            tcg_temp_free(t2); +            tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); +            tcg_temp_free(t1); +            /* operands of different sign, first operand and result different sign */ +            generate_exception(ctx, EXCP_OVERFLOW); +            gen_set_label(l1); +            gen_store_gpr(t0, rd); +            tcg_temp_free(t0); +        } +        opn = "sub"; +        break; +    case OPC_SUBU: +        if (rs != 0 && rt != 0) { +            tcg_gen_sub_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +        } else if (rs == 0 && rt != 0) { +            tcg_gen_neg_tl(cpu_gpr[rd], cpu_gpr[rt]); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +        } else if (rs != 0 && rt == 0) { +            tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +        } +        opn = "subu"; +        break; +#if defined(TARGET_MIPS64) +    case OPC_DADD: +        { +            TCGv t0 = tcg_temp_local_new(); +            TCGv t1 = tcg_temp_new(); +            TCGv t2 = tcg_temp_new(); +            TCGLabel *l1 = gen_new_label(); + +            gen_load_gpr(t1, rs); +            gen_load_gpr(t2, rt); +            tcg_gen_add_tl(t0, t1, t2); +            tcg_gen_xor_tl(t1, t1, t2); +            tcg_gen_xor_tl(t2, t0, t2); +            tcg_gen_andc_tl(t1, t2, t1); +            tcg_temp_free(t2); +            tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); +            tcg_temp_free(t1); +            /* operands of same sign, result different sign */ +            generate_exception(ctx, EXCP_OVERFLOW); +            gen_set_label(l1); +            gen_store_gpr(t0, rd); +            tcg_temp_free(t0); +        } +        opn = "dadd"; +        break; +    case OPC_DADDU: +        if (rs != 0 && rt != 0) { +            tcg_gen_add_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]); +        } else if (rs == 0 && rt != 0) { +            tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rt]); +        } else if (rs != 0 && rt == 0) { +            tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +        } +        opn = "daddu"; +        break; +    case OPC_DSUB: +        { +            TCGv t0 = tcg_temp_local_new(); +            TCGv t1 = tcg_temp_new(); +            TCGv t2 = tcg_temp_new(); +            TCGLabel *l1 = gen_new_label(); + +            gen_load_gpr(t1, rs); +            gen_load_gpr(t2, rt); +            tcg_gen_sub_tl(t0, t1, t2); +            tcg_gen_xor_tl(t2, t1, t2); +            tcg_gen_xor_tl(t1, t0, t1); +            tcg_gen_and_tl(t1, t1, t2); +            tcg_temp_free(t2); +            tcg_gen_brcondi_tl(TCG_COND_GE, t1, 0, l1); +            tcg_temp_free(t1); +            /* operands of different sign, first operand and result different sign */ +            generate_exception(ctx, EXCP_OVERFLOW); +            gen_set_label(l1); +            gen_store_gpr(t0, rd); +            tcg_temp_free(t0); +        } +        opn = "dsub"; +        break; +    case OPC_DSUBU: +        if (rs != 0 && rt != 0) { +            tcg_gen_sub_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]); +        } else if (rs == 0 && rt != 0) { +            tcg_gen_neg_tl(cpu_gpr[rd], cpu_gpr[rt]); +        } else if (rs != 0 && rt == 0) { +            tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +        } +        opn = "dsubu"; +        break; +#endif +    case OPC_MUL: +        if (likely(rs != 0 && rt != 0)) { +            tcg_gen_mul_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +        } +        opn = "mul"; +        break; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); +} + +/* Conditional move */ +static void gen_cond_move(DisasContext *ctx, uint32_t opc, +                          int rd, int rs, int rt) +{ +    const char *opn = "cond move"; +    TCGv t0, t1, t2; + +    if (rd == 0) { +        /* If no destination, treat it as a NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    gen_load_gpr(t0, rt); +    t1 = tcg_const_tl(0); +    t2 = tcg_temp_new(); +    gen_load_gpr(t2, rs); +    switch (opc) { +    case OPC_MOVN: +        tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rd], t0, t1, t2, cpu_gpr[rd]); +        opn = "movn"; +        break; +    case OPC_MOVZ: +        tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr[rd], t0, t1, t2, cpu_gpr[rd]); +        opn = "movz"; +        break; +    case OPC_SELNEZ: +        tcg_gen_movcond_tl(TCG_COND_NE, cpu_gpr[rd], t0, t1, t2, t1); +        opn = "selnez"; +        break; +    case OPC_SELEQZ: +        tcg_gen_movcond_tl(TCG_COND_EQ, cpu_gpr[rd], t0, t1, t2, t1); +        opn = "seleqz"; +        break; +    } +    tcg_temp_free(t2); +    tcg_temp_free(t1); +    tcg_temp_free(t0); + +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); +} + +/* Logic */ +static void gen_logic(DisasContext *ctx, uint32_t opc, +                      int rd, int rs, int rt) +{ +    const char *opn = "logic"; + +    if (rd == 0) { +        /* If no destination, treat it as a NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    switch (opc) { +    case OPC_AND: +        if (likely(rs != 0 && rt != 0)) { +            tcg_gen_and_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +        } +        opn = "and"; +        break; +    case OPC_NOR: +        if (rs != 0 && rt != 0) { +            tcg_gen_nor_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]); +        } else if (rs == 0 && rt != 0) { +            tcg_gen_not_tl(cpu_gpr[rd], cpu_gpr[rt]); +        } else if (rs != 0 && rt == 0) { +            tcg_gen_not_tl(cpu_gpr[rd], cpu_gpr[rs]); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rd], ~((target_ulong)0)); +        } +        opn = "nor"; +        break; +    case OPC_OR: +        if (likely(rs != 0 && rt != 0)) { +            tcg_gen_or_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]); +        } else if (rs == 0 && rt != 0) { +            tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rt]); +        } else if (rs != 0 && rt == 0) { +            tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +        } +        opn = "or"; +        break; +    case OPC_XOR: +        if (likely(rs != 0 && rt != 0)) { +            tcg_gen_xor_tl(cpu_gpr[rd], cpu_gpr[rs], cpu_gpr[rt]); +        } else if (rs == 0 && rt != 0) { +            tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rt]); +        } else if (rs != 0 && rt == 0) { +            tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]); +        } else { +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +        } +        opn = "xor"; +        break; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); +} + +/* Set on lower than */ +static void gen_slt(DisasContext *ctx, uint32_t opc, +                    int rd, int rs, int rt) +{ +    const char *opn = "slt"; +    TCGv t0, t1; + +    if (rd == 0) { +        /* If no destination, treat it as a NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); +    gen_load_gpr(t0, rs); +    gen_load_gpr(t1, rt); +    switch (opc) { +    case OPC_SLT: +        tcg_gen_setcond_tl(TCG_COND_LT, cpu_gpr[rd], t0, t1); +        opn = "slt"; +        break; +    case OPC_SLTU: +        tcg_gen_setcond_tl(TCG_COND_LTU, cpu_gpr[rd], t0, t1); +        opn = "sltu"; +        break; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +/* Shifts */ +static void gen_shift(DisasContext *ctx, uint32_t opc, +                      int rd, int rs, int rt) +{ +    const char *opn = "shifts"; +    TCGv t0, t1; + +    if (rd == 0) { +        /* If no destination, treat it as a NOP. +           For add & sub, we must generate the overflow exception when needed. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); +    gen_load_gpr(t0, rs); +    gen_load_gpr(t1, rt); +    switch (opc) { +    case OPC_SLLV: +        tcg_gen_andi_tl(t0, t0, 0x1f); +        tcg_gen_shl_tl(t0, t1, t0); +        tcg_gen_ext32s_tl(cpu_gpr[rd], t0); +        opn = "sllv"; +        break; +    case OPC_SRAV: +        tcg_gen_andi_tl(t0, t0, 0x1f); +        tcg_gen_sar_tl(cpu_gpr[rd], t1, t0); +        opn = "srav"; +        break; +    case OPC_SRLV: +        tcg_gen_ext32u_tl(t1, t1); +        tcg_gen_andi_tl(t0, t0, 0x1f); +        tcg_gen_shr_tl(t0, t1, t0); +        tcg_gen_ext32s_tl(cpu_gpr[rd], t0); +        opn = "srlv"; +        break; +    case OPC_ROTRV: +        { +            TCGv_i32 t2 = tcg_temp_new_i32(); +            TCGv_i32 t3 = tcg_temp_new_i32(); + +            tcg_gen_trunc_tl_i32(t2, t0); +            tcg_gen_trunc_tl_i32(t3, t1); +            tcg_gen_andi_i32(t2, t2, 0x1f); +            tcg_gen_rotr_i32(t2, t3, t2); +            tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); +            tcg_temp_free_i32(t2); +            tcg_temp_free_i32(t3); +            opn = "rotrv"; +        } +        break; +#if defined(TARGET_MIPS64) +    case OPC_DSLLV: +        tcg_gen_andi_tl(t0, t0, 0x3f); +        tcg_gen_shl_tl(cpu_gpr[rd], t1, t0); +        opn = "dsllv"; +        break; +    case OPC_DSRAV: +        tcg_gen_andi_tl(t0, t0, 0x3f); +        tcg_gen_sar_tl(cpu_gpr[rd], t1, t0); +        opn = "dsrav"; +        break; +    case OPC_DSRLV: +        tcg_gen_andi_tl(t0, t0, 0x3f); +        tcg_gen_shr_tl(cpu_gpr[rd], t1, t0); +        opn = "dsrlv"; +        break; +    case OPC_DROTRV: +        tcg_gen_andi_tl(t0, t0, 0x3f); +        tcg_gen_rotr_tl(cpu_gpr[rd], t1, t0); +        opn = "drotrv"; +        break; +#endif +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +/* Arithmetic on HI/LO registers */ +static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg) +{ +    const char *opn = "hilo"; + +    if (reg == 0 && (opc == OPC_MFHI || opc == OPC_MFLO)) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    if (acc != 0) { +        check_dsp(ctx); +    } + +    switch (opc) { +    case OPC_MFHI: +#if defined(TARGET_MIPS64) +        if (acc != 0) { +            tcg_gen_ext32s_tl(cpu_gpr[reg], cpu_HI[acc]); +        } else +#endif +        { +            tcg_gen_mov_tl(cpu_gpr[reg], cpu_HI[acc]); +        } +        opn = "mfhi"; +        break; +    case OPC_MFLO: +#if defined(TARGET_MIPS64) +        if (acc != 0) { +            tcg_gen_ext32s_tl(cpu_gpr[reg], cpu_LO[acc]); +        } else +#endif +        { +            tcg_gen_mov_tl(cpu_gpr[reg], cpu_LO[acc]); +        } +        opn = "mflo"; +        break; +    case OPC_MTHI: +        if (reg != 0) { +#if defined(TARGET_MIPS64) +            if (acc != 0) { +                tcg_gen_ext32s_tl(cpu_HI[acc], cpu_gpr[reg]); +            } else +#endif +            { +                tcg_gen_mov_tl(cpu_HI[acc], cpu_gpr[reg]); +            } +        } else { +            tcg_gen_movi_tl(cpu_HI[acc], 0); +        } +        opn = "mthi"; +        break; +    case OPC_MTLO: +        if (reg != 0) { +#if defined(TARGET_MIPS64) +            if (acc != 0) { +                tcg_gen_ext32s_tl(cpu_LO[acc], cpu_gpr[reg]); +            } else +#endif +            { +                tcg_gen_mov_tl(cpu_LO[acc], cpu_gpr[reg]); +            } +        } else { +            tcg_gen_movi_tl(cpu_LO[acc], 0); +        } +        opn = "mtlo"; +        break; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s", opn, regnames[reg]); +} + +static inline void gen_r6_ld(target_long addr, int reg, int memidx, +                             TCGMemOp memop) +{ +    TCGv t0 = tcg_const_tl(addr); +    tcg_gen_qemu_ld_tl(t0, t0, memidx, memop); +    gen_store_gpr(t0, reg); +    tcg_temp_free(t0); +} + +static inline void gen_pcrel(DisasContext *ctx, int opc, target_ulong pc, +                             int rs) +{ +    target_long offset; +    target_long addr; + +    switch (MASK_OPC_PCREL_TOP2BITS(opc)) { +    case OPC_ADDIUPC: +        if (rs != 0) { +            offset = sextract32(ctx->opcode << 2, 0, 21); +            addr = addr_add(ctx, pc, offset); +            tcg_gen_movi_tl(cpu_gpr[rs], addr); +        } +        break; +    case R6_OPC_LWPC: +        offset = sextract32(ctx->opcode << 2, 0, 21); +        addr = addr_add(ctx, pc, offset); +        gen_r6_ld(addr, rs, ctx->mem_idx, MO_TESL); +        break; +#if defined(TARGET_MIPS64) +    case OPC_LWUPC: +        check_mips_64(ctx); +        offset = sextract32(ctx->opcode << 2, 0, 21); +        addr = addr_add(ctx, pc, offset); +        gen_r6_ld(addr, rs, ctx->mem_idx, MO_TEUL); +        break; +#endif +    default: +        switch (MASK_OPC_PCREL_TOP5BITS(opc)) { +        case OPC_AUIPC: +            if (rs != 0) { +                offset = sextract32(ctx->opcode, 0, 16) << 16; +                addr = addr_add(ctx, pc, offset); +                tcg_gen_movi_tl(cpu_gpr[rs], addr); +            } +            break; +        case OPC_ALUIPC: +            if (rs != 0) { +                offset = sextract32(ctx->opcode, 0, 16) << 16; +                addr = ~0xFFFF & addr_add(ctx, pc, offset); +                tcg_gen_movi_tl(cpu_gpr[rs], addr); +            } +            break; +#if defined(TARGET_MIPS64) +        case R6_OPC_LDPC: /* bits 16 and 17 are part of immediate */ +        case R6_OPC_LDPC + (1 << 16): +        case R6_OPC_LDPC + (2 << 16): +        case R6_OPC_LDPC + (3 << 16): +            check_mips_64(ctx); +            offset = sextract32(ctx->opcode << 3, 0, 21); +            addr = addr_add(ctx, (pc & ~0x7), offset); +            gen_r6_ld(addr, rs, ctx->mem_idx, MO_TEQ); +            break; +#endif +        default: +            MIPS_INVAL("OPC_PCREL"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    } +} + +static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) +{ +    const char *opn = "r6 mul/div"; +    TCGv t0, t1; + +    if (rd == 0) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); + +    gen_load_gpr(t0, rs); +    gen_load_gpr(t1, rt); + +    switch (opc) { +    case R6_OPC_DIV: +        { +            TCGv t2 = tcg_temp_new(); +            TCGv t3 = tcg_temp_new(); +            tcg_gen_ext32s_tl(t0, t0); +            tcg_gen_ext32s_tl(t1, t1); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); +            tcg_gen_and_tl(t2, t2, t3); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); +            tcg_gen_or_tl(t2, t2, t3); +            tcg_gen_movi_tl(t3, 0); +            tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); +            tcg_gen_div_tl(cpu_gpr[rd], t0, t1); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "div"; +        break; +    case R6_OPC_MOD: +        { +            TCGv t2 = tcg_temp_new(); +            TCGv t3 = tcg_temp_new(); +            tcg_gen_ext32s_tl(t0, t0); +            tcg_gen_ext32s_tl(t1, t1); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); +            tcg_gen_and_tl(t2, t2, t3); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); +            tcg_gen_or_tl(t2, t2, t3); +            tcg_gen_movi_tl(t3, 0); +            tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); +            tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "mod"; +        break; +    case R6_OPC_DIVU: +        { +            TCGv t2 = tcg_const_tl(0); +            TCGv t3 = tcg_const_tl(1); +            tcg_gen_ext32u_tl(t0, t0); +            tcg_gen_ext32u_tl(t1, t1); +            tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); +            tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "divu"; +        break; +    case R6_OPC_MODU: +        { +            TCGv t2 = tcg_const_tl(0); +            TCGv t3 = tcg_const_tl(1); +            tcg_gen_ext32u_tl(t0, t0); +            tcg_gen_ext32u_tl(t1, t1); +            tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); +            tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "modu"; +        break; +    case R6_OPC_MUL: +        { +            TCGv_i32 t2 = tcg_temp_new_i32(); +            TCGv_i32 t3 = tcg_temp_new_i32(); +            tcg_gen_trunc_tl_i32(t2, t0); +            tcg_gen_trunc_tl_i32(t3, t1); +            tcg_gen_mul_i32(t2, t2, t3); +            tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); +            tcg_temp_free_i32(t2); +            tcg_temp_free_i32(t3); +        } +        opn = "mul"; +        break; +    case R6_OPC_MUH: +        { +            TCGv_i32 t2 = tcg_temp_new_i32(); +            TCGv_i32 t3 = tcg_temp_new_i32(); +            tcg_gen_trunc_tl_i32(t2, t0); +            tcg_gen_trunc_tl_i32(t3, t1); +            tcg_gen_muls2_i32(t2, t3, t2, t3); +            tcg_gen_ext_i32_tl(cpu_gpr[rd], t3); +            tcg_temp_free_i32(t2); +            tcg_temp_free_i32(t3); +        } +        opn = "muh"; +        break; +    case R6_OPC_MULU: +        { +            TCGv_i32 t2 = tcg_temp_new_i32(); +            TCGv_i32 t3 = tcg_temp_new_i32(); +            tcg_gen_trunc_tl_i32(t2, t0); +            tcg_gen_trunc_tl_i32(t3, t1); +            tcg_gen_mul_i32(t2, t2, t3); +            tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); +            tcg_temp_free_i32(t2); +            tcg_temp_free_i32(t3); +        } +        opn = "mulu"; +        break; +    case R6_OPC_MUHU: +        { +            TCGv_i32 t2 = tcg_temp_new_i32(); +            TCGv_i32 t3 = tcg_temp_new_i32(); +            tcg_gen_trunc_tl_i32(t2, t0); +            tcg_gen_trunc_tl_i32(t3, t1); +            tcg_gen_mulu2_i32(t2, t3, t2, t3); +            tcg_gen_ext_i32_tl(cpu_gpr[rd], t3); +            tcg_temp_free_i32(t2); +            tcg_temp_free_i32(t3); +        } +        opn = "muhu"; +        break; +#if defined(TARGET_MIPS64) +    case R6_OPC_DDIV: +        { +            TCGv t2 = tcg_temp_new(); +            TCGv t3 = tcg_temp_new(); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, -1LL << 63); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1LL); +            tcg_gen_and_tl(t2, t2, t3); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); +            tcg_gen_or_tl(t2, t2, t3); +            tcg_gen_movi_tl(t3, 0); +            tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); +            tcg_gen_div_tl(cpu_gpr[rd], t0, t1); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "ddiv"; +        break; +    case R6_OPC_DMOD: +        { +            TCGv t2 = tcg_temp_new(); +            TCGv t3 = tcg_temp_new(); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, -1LL << 63); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1LL); +            tcg_gen_and_tl(t2, t2, t3); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); +            tcg_gen_or_tl(t2, t2, t3); +            tcg_gen_movi_tl(t3, 0); +            tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); +            tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "dmod"; +        break; +    case R6_OPC_DDIVU: +        { +            TCGv t2 = tcg_const_tl(0); +            TCGv t3 = tcg_const_tl(1); +            tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); +            tcg_gen_divu_i64(cpu_gpr[rd], t0, t1); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "ddivu"; +        break; +    case R6_OPC_DMODU: +        { +            TCGv t2 = tcg_const_tl(0); +            TCGv t3 = tcg_const_tl(1); +            tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); +            tcg_gen_remu_i64(cpu_gpr[rd], t0, t1); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "dmodu"; +        break; +    case R6_OPC_DMUL: +        tcg_gen_mul_i64(cpu_gpr[rd], t0, t1); +        opn = "dmul"; +        break; +    case R6_OPC_DMUH: +        { +            TCGv t2 = tcg_temp_new(); +            tcg_gen_muls2_i64(t2, cpu_gpr[rd], t0, t1); +            tcg_temp_free(t2); +        } +        opn = "dmuh"; +        break; +    case R6_OPC_DMULU: +        tcg_gen_mul_i64(cpu_gpr[rd], t0, t1); +        opn = "dmulu"; +        break; +    case R6_OPC_DMUHU: +        { +            TCGv t2 = tcg_temp_new(); +            tcg_gen_mulu2_i64(t2, cpu_gpr[rd], t0, t1); +            tcg_temp_free(t2); +        } +        opn = "dmuhu"; +        break; +#endif +    default: +        MIPS_INVAL(opn); +        generate_exception(ctx, EXCP_RI); +        goto out; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]); + out: +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +static void gen_muldiv(DisasContext *ctx, uint32_t opc, +                       int acc, int rs, int rt) +{ +    const char *opn = "mul/div"; +    TCGv t0, t1; + +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); + +    gen_load_gpr(t0, rs); +    gen_load_gpr(t1, rt); + +    if (acc != 0) { +        check_dsp(ctx); +    } + +    switch (opc) { +    case OPC_DIV: +        { +            TCGv t2 = tcg_temp_new(); +            TCGv t3 = tcg_temp_new(); +            tcg_gen_ext32s_tl(t0, t0); +            tcg_gen_ext32s_tl(t1, t1); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); +            tcg_gen_and_tl(t2, t2, t3); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); +            tcg_gen_or_tl(t2, t2, t3); +            tcg_gen_movi_tl(t3, 0); +            tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); +            tcg_gen_div_tl(cpu_LO[acc], t0, t1); +            tcg_gen_rem_tl(cpu_HI[acc], t0, t1); +            tcg_gen_ext32s_tl(cpu_LO[acc], cpu_LO[acc]); +            tcg_gen_ext32s_tl(cpu_HI[acc], cpu_HI[acc]); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "div"; +        break; +    case OPC_DIVU: +        { +            TCGv t2 = tcg_const_tl(0); +            TCGv t3 = tcg_const_tl(1); +            tcg_gen_ext32u_tl(t0, t0); +            tcg_gen_ext32u_tl(t1, t1); +            tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); +            tcg_gen_divu_tl(cpu_LO[acc], t0, t1); +            tcg_gen_remu_tl(cpu_HI[acc], t0, t1); +            tcg_gen_ext32s_tl(cpu_LO[acc], cpu_LO[acc]); +            tcg_gen_ext32s_tl(cpu_HI[acc], cpu_HI[acc]); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "divu"; +        break; +    case OPC_MULT: +        { +            TCGv_i32 t2 = tcg_temp_new_i32(); +            TCGv_i32 t3 = tcg_temp_new_i32(); +            tcg_gen_trunc_tl_i32(t2, t0); +            tcg_gen_trunc_tl_i32(t3, t1); +            tcg_gen_muls2_i32(t2, t3, t2, t3); +            tcg_gen_ext_i32_tl(cpu_LO[acc], t2); +            tcg_gen_ext_i32_tl(cpu_HI[acc], t3); +            tcg_temp_free_i32(t2); +            tcg_temp_free_i32(t3); +        } +        opn = "mult"; +        break; +    case OPC_MULTU: +        { +            TCGv_i32 t2 = tcg_temp_new_i32(); +            TCGv_i32 t3 = tcg_temp_new_i32(); +            tcg_gen_trunc_tl_i32(t2, t0); +            tcg_gen_trunc_tl_i32(t3, t1); +            tcg_gen_mulu2_i32(t2, t3, t2, t3); +            tcg_gen_ext_i32_tl(cpu_LO[acc], t2); +            tcg_gen_ext_i32_tl(cpu_HI[acc], t3); +            tcg_temp_free_i32(t2); +            tcg_temp_free_i32(t3); +        } +        opn = "multu"; +        break; +#if defined(TARGET_MIPS64) +    case OPC_DDIV: +        { +            TCGv t2 = tcg_temp_new(); +            TCGv t3 = tcg_temp_new(); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, -1LL << 63); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1LL); +            tcg_gen_and_tl(t2, t2, t3); +            tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); +            tcg_gen_or_tl(t2, t2, t3); +            tcg_gen_movi_tl(t3, 0); +            tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); +            tcg_gen_div_tl(cpu_LO[acc], t0, t1); +            tcg_gen_rem_tl(cpu_HI[acc], t0, t1); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "ddiv"; +        break; +    case OPC_DDIVU: +        { +            TCGv t2 = tcg_const_tl(0); +            TCGv t3 = tcg_const_tl(1); +            tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); +            tcg_gen_divu_i64(cpu_LO[acc], t0, t1); +            tcg_gen_remu_i64(cpu_HI[acc], t0, t1); +            tcg_temp_free(t3); +            tcg_temp_free(t2); +        } +        opn = "ddivu"; +        break; +    case OPC_DMULT: +        tcg_gen_muls2_i64(cpu_LO[acc], cpu_HI[acc], t0, t1); +        opn = "dmult"; +        break; +    case OPC_DMULTU: +        tcg_gen_mulu2_i64(cpu_LO[acc], cpu_HI[acc], t0, t1); +        opn = "dmultu"; +        break; +#endif +    case OPC_MADD: +        { +            TCGv_i64 t2 = tcg_temp_new_i64(); +            TCGv_i64 t3 = tcg_temp_new_i64(); + +            tcg_gen_ext_tl_i64(t2, t0); +            tcg_gen_ext_tl_i64(t3, t1); +            tcg_gen_mul_i64(t2, t2, t3); +            tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); +            tcg_gen_add_i64(t2, t2, t3); +            tcg_temp_free_i64(t3); +            tcg_gen_trunc_i64_tl(t0, t2); +            tcg_gen_shri_i64(t2, t2, 32); +            tcg_gen_trunc_i64_tl(t1, t2); +            tcg_temp_free_i64(t2); +            tcg_gen_ext32s_tl(cpu_LO[acc], t0); +            tcg_gen_ext32s_tl(cpu_HI[acc], t1); +        } +        opn = "madd"; +        break; +    case OPC_MADDU: +        { +            TCGv_i64 t2 = tcg_temp_new_i64(); +            TCGv_i64 t3 = tcg_temp_new_i64(); + +            tcg_gen_ext32u_tl(t0, t0); +            tcg_gen_ext32u_tl(t1, t1); +            tcg_gen_extu_tl_i64(t2, t0); +            tcg_gen_extu_tl_i64(t3, t1); +            tcg_gen_mul_i64(t2, t2, t3); +            tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); +            tcg_gen_add_i64(t2, t2, t3); +            tcg_temp_free_i64(t3); +            tcg_gen_trunc_i64_tl(t0, t2); +            tcg_gen_shri_i64(t2, t2, 32); +            tcg_gen_trunc_i64_tl(t1, t2); +            tcg_temp_free_i64(t2); +            tcg_gen_ext32s_tl(cpu_LO[acc], t0); +            tcg_gen_ext32s_tl(cpu_HI[acc], t1); +        } +        opn = "maddu"; +        break; +    case OPC_MSUB: +        { +            TCGv_i64 t2 = tcg_temp_new_i64(); +            TCGv_i64 t3 = tcg_temp_new_i64(); + +            tcg_gen_ext_tl_i64(t2, t0); +            tcg_gen_ext_tl_i64(t3, t1); +            tcg_gen_mul_i64(t2, t2, t3); +            tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); +            tcg_gen_sub_i64(t2, t3, t2); +            tcg_temp_free_i64(t3); +            tcg_gen_trunc_i64_tl(t0, t2); +            tcg_gen_shri_i64(t2, t2, 32); +            tcg_gen_trunc_i64_tl(t1, t2); +            tcg_temp_free_i64(t2); +            tcg_gen_ext32s_tl(cpu_LO[acc], t0); +            tcg_gen_ext32s_tl(cpu_HI[acc], t1); +        } +        opn = "msub"; +        break; +    case OPC_MSUBU: +        { +            TCGv_i64 t2 = tcg_temp_new_i64(); +            TCGv_i64 t3 = tcg_temp_new_i64(); + +            tcg_gen_ext32u_tl(t0, t0); +            tcg_gen_ext32u_tl(t1, t1); +            tcg_gen_extu_tl_i64(t2, t0); +            tcg_gen_extu_tl_i64(t3, t1); +            tcg_gen_mul_i64(t2, t2, t3); +            tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); +            tcg_gen_sub_i64(t2, t3, t2); +            tcg_temp_free_i64(t3); +            tcg_gen_trunc_i64_tl(t0, t2); +            tcg_gen_shri_i64(t2, t2, 32); +            tcg_gen_trunc_i64_tl(t1, t2); +            tcg_temp_free_i64(t2); +            tcg_gen_ext32s_tl(cpu_LO[acc], t0); +            tcg_gen_ext32s_tl(cpu_HI[acc], t1); +        } +        opn = "msubu"; +        break; +    default: +        MIPS_INVAL(opn); +        generate_exception(ctx, EXCP_RI); +        goto out; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s %s", opn, regnames[rs], regnames[rt]); + out: +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +static void gen_mul_vr54xx (DisasContext *ctx, uint32_t opc, +                            int rd, int rs, int rt) +{ +    const char *opn = "mul vr54xx"; +    TCGv t0 = tcg_temp_new(); +    TCGv t1 = tcg_temp_new(); + +    gen_load_gpr(t0, rs); +    gen_load_gpr(t1, rt); + +    switch (opc) { +    case OPC_VR54XX_MULS: +        gen_helper_muls(t0, cpu_env, t0, t1); +        opn = "muls"; +        break; +    case OPC_VR54XX_MULSU: +        gen_helper_mulsu(t0, cpu_env, t0, t1); +        opn = "mulsu"; +        break; +    case OPC_VR54XX_MACC: +        gen_helper_macc(t0, cpu_env, t0, t1); +        opn = "macc"; +        break; +    case OPC_VR54XX_MACCU: +        gen_helper_maccu(t0, cpu_env, t0, t1); +        opn = "maccu"; +        break; +    case OPC_VR54XX_MSAC: +        gen_helper_msac(t0, cpu_env, t0, t1); +        opn = "msac"; +        break; +    case OPC_VR54XX_MSACU: +        gen_helper_msacu(t0, cpu_env, t0, t1); +        opn = "msacu"; +        break; +    case OPC_VR54XX_MULHI: +        gen_helper_mulhi(t0, cpu_env, t0, t1); +        opn = "mulhi"; +        break; +    case OPC_VR54XX_MULHIU: +        gen_helper_mulhiu(t0, cpu_env, t0, t1); +        opn = "mulhiu"; +        break; +    case OPC_VR54XX_MULSHI: +        gen_helper_mulshi(t0, cpu_env, t0, t1); +        opn = "mulshi"; +        break; +    case OPC_VR54XX_MULSHIU: +        gen_helper_mulshiu(t0, cpu_env, t0, t1); +        opn = "mulshiu"; +        break; +    case OPC_VR54XX_MACCHI: +        gen_helper_macchi(t0, cpu_env, t0, t1); +        opn = "macchi"; +        break; +    case OPC_VR54XX_MACCHIU: +        gen_helper_macchiu(t0, cpu_env, t0, t1); +        opn = "macchiu"; +        break; +    case OPC_VR54XX_MSACHI: +        gen_helper_msachi(t0, cpu_env, t0, t1); +        opn = "msachi"; +        break; +    case OPC_VR54XX_MSACHIU: +        gen_helper_msachiu(t0, cpu_env, t0, t1); +        opn = "msachiu"; +        break; +    default: +        MIPS_INVAL("mul vr54xx"); +        generate_exception(ctx, EXCP_RI); +        goto out; +    } +    gen_store_gpr(t0, rd); +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, %s", opn, regnames[rd], regnames[rs], regnames[rt]); + + out: +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +static void gen_cl (DisasContext *ctx, uint32_t opc, +                    int rd, int rs) +{ +    const char *opn = "CLx"; +    TCGv t0; + +    if (rd == 0) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } +    t0 = tcg_temp_new(); +    gen_load_gpr(t0, rs); +    switch (opc) { +    case OPC_CLO: +    case R6_OPC_CLO: +        gen_helper_clo(cpu_gpr[rd], t0); +        opn = "clo"; +        break; +    case OPC_CLZ: +    case R6_OPC_CLZ: +        gen_helper_clz(cpu_gpr[rd], t0); +        opn = "clz"; +        break; +#if defined(TARGET_MIPS64) +    case OPC_DCLO: +    case R6_OPC_DCLO: +        gen_helper_dclo(cpu_gpr[rd], t0); +        opn = "dclo"; +        break; +    case OPC_DCLZ: +    case R6_OPC_DCLZ: +        gen_helper_dclz(cpu_gpr[rd], t0); +        opn = "dclz"; +        break; +#endif +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]); +    tcg_temp_free(t0); +} + +/* Godson integer instructions */ +static void gen_loongson_integer(DisasContext *ctx, uint32_t opc, +                                 int rd, int rs, int rt) +{ +    const char *opn = "loongson"; +    TCGv t0, t1; + +    if (rd == 0) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    switch (opc) { +    case OPC_MULT_G_2E: +    case OPC_MULT_G_2F: +    case OPC_MULTU_G_2E: +    case OPC_MULTU_G_2F: +#if defined(TARGET_MIPS64) +    case OPC_DMULT_G_2E: +    case OPC_DMULT_G_2F: +    case OPC_DMULTU_G_2E: +    case OPC_DMULTU_G_2F: +#endif +        t0 = tcg_temp_new(); +        t1 = tcg_temp_new(); +        break; +    default: +        t0 = tcg_temp_local_new(); +        t1 = tcg_temp_local_new(); +        break; +    } + +    gen_load_gpr(t0, rs); +    gen_load_gpr(t1, rt); + +    switch (opc) { +    case OPC_MULT_G_2E: +    case OPC_MULT_G_2F: +        tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); +        tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +        opn = "mult.g"; +        break; +    case OPC_MULTU_G_2E: +    case OPC_MULTU_G_2F: +        tcg_gen_ext32u_tl(t0, t0); +        tcg_gen_ext32u_tl(t1, t1); +        tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); +        tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +        opn = "multu.g"; +        break; +    case OPC_DIV_G_2E: +    case OPC_DIV_G_2F: +        { +            TCGLabel *l1 = gen_new_label(); +            TCGLabel *l2 = gen_new_label(); +            TCGLabel *l3 = gen_new_label(); +            tcg_gen_ext32s_tl(t0, t0); +            tcg_gen_ext32s_tl(t1, t1); +            tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +            tcg_gen_br(l3); +            gen_set_label(l1); +            tcg_gen_brcondi_tl(TCG_COND_NE, t0, INT_MIN, l2); +            tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1, l2); +            tcg_gen_mov_tl(cpu_gpr[rd], t0); +            tcg_gen_br(l3); +            gen_set_label(l2); +            tcg_gen_div_tl(cpu_gpr[rd], t0, t1); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +            gen_set_label(l3); +        } +        opn = "div.g"; +        break; +    case OPC_DIVU_G_2E: +    case OPC_DIVU_G_2F: +        { +            TCGLabel *l1 = gen_new_label(); +            TCGLabel *l2 = gen_new_label(); +            tcg_gen_ext32u_tl(t0, t0); +            tcg_gen_ext32u_tl(t1, t1); +            tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +            tcg_gen_br(l2); +            gen_set_label(l1); +            tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +            gen_set_label(l2); +        } +        opn = "divu.g"; +        break; +    case OPC_MOD_G_2E: +    case OPC_MOD_G_2F: +        { +            TCGLabel *l1 = gen_new_label(); +            TCGLabel *l2 = gen_new_label(); +            TCGLabel *l3 = gen_new_label(); +            tcg_gen_ext32u_tl(t0, t0); +            tcg_gen_ext32u_tl(t1, t1); +            tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); +            tcg_gen_brcondi_tl(TCG_COND_NE, t0, INT_MIN, l2); +            tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1, l2); +            gen_set_label(l1); +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +            tcg_gen_br(l3); +            gen_set_label(l2); +            tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +            gen_set_label(l3); +        } +        opn = "mod.g"; +        break; +    case OPC_MODU_G_2E: +    case OPC_MODU_G_2F: +        { +            TCGLabel *l1 = gen_new_label(); +            TCGLabel *l2 = gen_new_label(); +            tcg_gen_ext32u_tl(t0, t0); +            tcg_gen_ext32u_tl(t1, t1); +            tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +            tcg_gen_br(l2); +            gen_set_label(l1); +            tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); +            tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +            gen_set_label(l2); +        } +        opn = "modu.g"; +        break; +#if defined(TARGET_MIPS64) +    case OPC_DMULT_G_2E: +    case OPC_DMULT_G_2F: +        tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); +        opn = "dmult.g"; +        break; +    case OPC_DMULTU_G_2E: +    case OPC_DMULTU_G_2F: +        tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); +        opn = "dmultu.g"; +        break; +    case OPC_DDIV_G_2E: +    case OPC_DDIV_G_2F: +        { +            TCGLabel *l1 = gen_new_label(); +            TCGLabel *l2 = gen_new_label(); +            TCGLabel *l3 = gen_new_label(); +            tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +            tcg_gen_br(l3); +            gen_set_label(l1); +            tcg_gen_brcondi_tl(TCG_COND_NE, t0, -1LL << 63, l2); +            tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); +            tcg_gen_mov_tl(cpu_gpr[rd], t0); +            tcg_gen_br(l3); +            gen_set_label(l2); +            tcg_gen_div_tl(cpu_gpr[rd], t0, t1); +            gen_set_label(l3); +        } +        opn = "ddiv.g"; +        break; +    case OPC_DDIVU_G_2E: +    case OPC_DDIVU_G_2F: +        { +            TCGLabel *l1 = gen_new_label(); +            TCGLabel *l2 = gen_new_label(); +            tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +            tcg_gen_br(l2); +            gen_set_label(l1); +            tcg_gen_divu_tl(cpu_gpr[rd], t0, t1); +            gen_set_label(l2); +        } +        opn = "ddivu.g"; +        break; +    case OPC_DMOD_G_2E: +    case OPC_DMOD_G_2F: +        { +            TCGLabel *l1 = gen_new_label(); +            TCGLabel *l2 = gen_new_label(); +            TCGLabel *l3 = gen_new_label(); +            tcg_gen_brcondi_tl(TCG_COND_EQ, t1, 0, l1); +            tcg_gen_brcondi_tl(TCG_COND_NE, t0, -1LL << 63, l2); +            tcg_gen_brcondi_tl(TCG_COND_NE, t1, -1LL, l2); +            gen_set_label(l1); +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +            tcg_gen_br(l3); +            gen_set_label(l2); +            tcg_gen_rem_tl(cpu_gpr[rd], t0, t1); +            gen_set_label(l3); +        } +        opn = "dmod.g"; +        break; +    case OPC_DMODU_G_2E: +    case OPC_DMODU_G_2F: +        { +            TCGLabel *l1 = gen_new_label(); +            TCGLabel *l2 = gen_new_label(); +            tcg_gen_brcondi_tl(TCG_COND_NE, t1, 0, l1); +            tcg_gen_movi_tl(cpu_gpr[rd], 0); +            tcg_gen_br(l2); +            gen_set_label(l1); +            tcg_gen_remu_tl(cpu_gpr[rd], t0, t1); +            gen_set_label(l2); +        } +        opn = "dmodu.g"; +        break; +#endif +    } + +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s", opn, regnames[rd], regnames[rs]); +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +/* Loongson multimedia instructions */ +static void gen_loongson_multimedia(DisasContext *ctx, int rd, int rs, int rt) +{ +    const char *opn = "loongson_cp2"; +    uint32_t opc, shift_max; +    TCGv_i64 t0, t1; + +    opc = MASK_LMI(ctx->opcode); +    switch (opc) { +    case OPC_ADD_CP2: +    case OPC_SUB_CP2: +    case OPC_DADD_CP2: +    case OPC_DSUB_CP2: +        t0 = tcg_temp_local_new_i64(); +        t1 = tcg_temp_local_new_i64(); +        break; +    default: +        t0 = tcg_temp_new_i64(); +        t1 = tcg_temp_new_i64(); +        break; +    } + +    gen_load_fpr64(ctx, t0, rs); +    gen_load_fpr64(ctx, t1, rt); + +#define LMI_HELPER(UP, LO) \ +    case OPC_##UP: gen_helper_##LO(t0, t0, t1); opn = #LO; break +#define LMI_HELPER_1(UP, LO) \ +    case OPC_##UP: gen_helper_##LO(t0, t0); opn = #LO; break +#define LMI_DIRECT(UP, LO, OP) \ +    case OPC_##UP: tcg_gen_##OP##_i64(t0, t0, t1); opn = #LO; break + +    switch (opc) { +    LMI_HELPER(PADDSH, paddsh); +    LMI_HELPER(PADDUSH, paddush); +    LMI_HELPER(PADDH, paddh); +    LMI_HELPER(PADDW, paddw); +    LMI_HELPER(PADDSB, paddsb); +    LMI_HELPER(PADDUSB, paddusb); +    LMI_HELPER(PADDB, paddb); + +    LMI_HELPER(PSUBSH, psubsh); +    LMI_HELPER(PSUBUSH, psubush); +    LMI_HELPER(PSUBH, psubh); +    LMI_HELPER(PSUBW, psubw); +    LMI_HELPER(PSUBSB, psubsb); +    LMI_HELPER(PSUBUSB, psubusb); +    LMI_HELPER(PSUBB, psubb); + +    LMI_HELPER(PSHUFH, pshufh); +    LMI_HELPER(PACKSSWH, packsswh); +    LMI_HELPER(PACKSSHB, packsshb); +    LMI_HELPER(PACKUSHB, packushb); + +    LMI_HELPER(PUNPCKLHW, punpcklhw); +    LMI_HELPER(PUNPCKHHW, punpckhhw); +    LMI_HELPER(PUNPCKLBH, punpcklbh); +    LMI_HELPER(PUNPCKHBH, punpckhbh); +    LMI_HELPER(PUNPCKLWD, punpcklwd); +    LMI_HELPER(PUNPCKHWD, punpckhwd); + +    LMI_HELPER(PAVGH, pavgh); +    LMI_HELPER(PAVGB, pavgb); +    LMI_HELPER(PMAXSH, pmaxsh); +    LMI_HELPER(PMINSH, pminsh); +    LMI_HELPER(PMAXUB, pmaxub); +    LMI_HELPER(PMINUB, pminub); + +    LMI_HELPER(PCMPEQW, pcmpeqw); +    LMI_HELPER(PCMPGTW, pcmpgtw); +    LMI_HELPER(PCMPEQH, pcmpeqh); +    LMI_HELPER(PCMPGTH, pcmpgth); +    LMI_HELPER(PCMPEQB, pcmpeqb); +    LMI_HELPER(PCMPGTB, pcmpgtb); + +    LMI_HELPER(PSLLW, psllw); +    LMI_HELPER(PSLLH, psllh); +    LMI_HELPER(PSRLW, psrlw); +    LMI_HELPER(PSRLH, psrlh); +    LMI_HELPER(PSRAW, psraw); +    LMI_HELPER(PSRAH, psrah); + +    LMI_HELPER(PMULLH, pmullh); +    LMI_HELPER(PMULHH, pmulhh); +    LMI_HELPER(PMULHUH, pmulhuh); +    LMI_HELPER(PMADDHW, pmaddhw); + +    LMI_HELPER(PASUBUB, pasubub); +    LMI_HELPER_1(BIADD, biadd); +    LMI_HELPER_1(PMOVMSKB, pmovmskb); + +    LMI_DIRECT(PADDD, paddd, add); +    LMI_DIRECT(PSUBD, psubd, sub); +    LMI_DIRECT(XOR_CP2, xor, xor); +    LMI_DIRECT(NOR_CP2, nor, nor); +    LMI_DIRECT(AND_CP2, and, and); +    LMI_DIRECT(PANDN, pandn, andc); +    LMI_DIRECT(OR, or, or); + +    case OPC_PINSRH_0: +        tcg_gen_deposit_i64(t0, t0, t1, 0, 16); +        opn = "pinsrh_0"; +        break; +    case OPC_PINSRH_1: +        tcg_gen_deposit_i64(t0, t0, t1, 16, 16); +        opn = "pinsrh_1"; +        break; +    case OPC_PINSRH_2: +        tcg_gen_deposit_i64(t0, t0, t1, 32, 16); +        opn = "pinsrh_2"; +        break; +    case OPC_PINSRH_3: +        tcg_gen_deposit_i64(t0, t0, t1, 48, 16); +        opn = "pinsrh_3"; +        break; + +    case OPC_PEXTRH: +        tcg_gen_andi_i64(t1, t1, 3); +        tcg_gen_shli_i64(t1, t1, 4); +        tcg_gen_shr_i64(t0, t0, t1); +        tcg_gen_ext16u_i64(t0, t0); +        opn = "pextrh"; +        break; + +    case OPC_ADDU_CP2: +        tcg_gen_add_i64(t0, t0, t1); +        tcg_gen_ext32s_i64(t0, t0); +        opn = "addu"; +        break; +    case OPC_SUBU_CP2: +        tcg_gen_sub_i64(t0, t0, t1); +        tcg_gen_ext32s_i64(t0, t0); +        opn = "addu"; +        break; + +    case OPC_SLL_CP2: +        opn = "sll"; +        shift_max = 32; +        goto do_shift; +    case OPC_SRL_CP2: +        opn = "srl"; +        shift_max = 32; +        goto do_shift; +    case OPC_SRA_CP2: +        opn = "sra"; +        shift_max = 32; +        goto do_shift; +    case OPC_DSLL_CP2: +        opn = "dsll"; +        shift_max = 64; +        goto do_shift; +    case OPC_DSRL_CP2: +        opn = "dsrl"; +        shift_max = 64; +        goto do_shift; +    case OPC_DSRA_CP2: +        opn = "dsra"; +        shift_max = 64; +        goto do_shift; +    do_shift: +        /* Make sure shift count isn't TCG undefined behaviour.  */ +        tcg_gen_andi_i64(t1, t1, shift_max - 1); + +        switch (opc) { +        case OPC_SLL_CP2: +        case OPC_DSLL_CP2: +            tcg_gen_shl_i64(t0, t0, t1); +            break; +        case OPC_SRA_CP2: +        case OPC_DSRA_CP2: +            /* Since SRA is UndefinedResult without sign-extended inputs, +               we can treat SRA and DSRA the same.  */ +            tcg_gen_sar_i64(t0, t0, t1); +            break; +        case OPC_SRL_CP2: +            /* We want to shift in zeros for SRL; zero-extend first.  */ +            tcg_gen_ext32u_i64(t0, t0); +            /* FALLTHRU */ +        case OPC_DSRL_CP2: +            tcg_gen_shr_i64(t0, t0, t1); +            break; +        } + +        if (shift_max == 32) { +            tcg_gen_ext32s_i64(t0, t0); +        } + +        /* Shifts larger than MAX produce zero.  */ +        tcg_gen_setcondi_i64(TCG_COND_LTU, t1, t1, shift_max); +        tcg_gen_neg_i64(t1, t1); +        tcg_gen_and_i64(t0, t0, t1); +        break; + +    case OPC_ADD_CP2: +    case OPC_DADD_CP2: +        { +            TCGv_i64 t2 = tcg_temp_new_i64(); +            TCGLabel *lab = gen_new_label(); + +            tcg_gen_mov_i64(t2, t0); +            tcg_gen_add_i64(t0, t1, t2); +            if (opc == OPC_ADD_CP2) { +                tcg_gen_ext32s_i64(t0, t0); +            } +            tcg_gen_xor_i64(t1, t1, t2); +            tcg_gen_xor_i64(t2, t2, t0); +            tcg_gen_andc_i64(t1, t2, t1); +            tcg_temp_free_i64(t2); +            tcg_gen_brcondi_i64(TCG_COND_GE, t1, 0, lab); +            generate_exception(ctx, EXCP_OVERFLOW); +            gen_set_label(lab); + +            opn = (opc == OPC_ADD_CP2 ? "add" : "dadd"); +            break; +        } + +    case OPC_SUB_CP2: +    case OPC_DSUB_CP2: +        { +            TCGv_i64 t2 = tcg_temp_new_i64(); +            TCGLabel *lab = gen_new_label(); + +            tcg_gen_mov_i64(t2, t0); +            tcg_gen_sub_i64(t0, t1, t2); +            if (opc == OPC_SUB_CP2) { +                tcg_gen_ext32s_i64(t0, t0); +            } +            tcg_gen_xor_i64(t1, t1, t2); +            tcg_gen_xor_i64(t2, t2, t0); +            tcg_gen_and_i64(t1, t1, t2); +            tcg_temp_free_i64(t2); +            tcg_gen_brcondi_i64(TCG_COND_GE, t1, 0, lab); +            generate_exception(ctx, EXCP_OVERFLOW); +            gen_set_label(lab); + +            opn = (opc == OPC_SUB_CP2 ? "sub" : "dsub"); +            break; +        } + +    case OPC_PMULUW: +        tcg_gen_ext32u_i64(t0, t0); +        tcg_gen_ext32u_i64(t1, t1); +        tcg_gen_mul_i64(t0, t0, t1); +        opn = "pmuluw"; +        break; + +    case OPC_SEQU_CP2: +    case OPC_SEQ_CP2: +    case OPC_SLTU_CP2: +    case OPC_SLT_CP2: +    case OPC_SLEU_CP2: +    case OPC_SLE_CP2: +        /* ??? Document is unclear: Set FCC[CC].  Does that mean the +           FD field is the CC field?  */ +    default: +        MIPS_INVAL(opn); +        generate_exception(ctx, EXCP_RI); +        return; +    } + +#undef LMI_HELPER +#undef LMI_DIRECT + +    gen_store_fpr64(ctx, t0, rd); + +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, %s", opn, +               fregnames[rd], fregnames[rs], fregnames[rt]); +    tcg_temp_free_i64(t0); +    tcg_temp_free_i64(t1); +} + +/* Traps */ +static void gen_trap (DisasContext *ctx, uint32_t opc, +                      int rs, int rt, int16_t imm) +{ +    int cond; +    TCGv t0 = tcg_temp_new(); +    TCGv t1 = tcg_temp_new(); + +    cond = 0; +    /* Load needed operands */ +    switch (opc) { +    case OPC_TEQ: +    case OPC_TGE: +    case OPC_TGEU: +    case OPC_TLT: +    case OPC_TLTU: +    case OPC_TNE: +        /* Compare two registers */ +        if (rs != rt) { +            gen_load_gpr(t0, rs); +            gen_load_gpr(t1, rt); +            cond = 1; +        } +        break; +    case OPC_TEQI: +    case OPC_TGEI: +    case OPC_TGEIU: +    case OPC_TLTI: +    case OPC_TLTIU: +    case OPC_TNEI: +        /* Compare register to immediate */ +        if (rs != 0 || imm != 0) { +            gen_load_gpr(t0, rs); +            tcg_gen_movi_tl(t1, (int32_t)imm); +            cond = 1; +        } +        break; +    } +    if (cond == 0) { +        switch (opc) { +        case OPC_TEQ:   /* rs == rs */ +        case OPC_TEQI:  /* r0 == 0  */ +        case OPC_TGE:   /* rs >= rs */ +        case OPC_TGEI:  /* r0 >= 0  */ +        case OPC_TGEU:  /* rs >= rs unsigned */ +        case OPC_TGEIU: /* r0 >= 0  unsigned */ +            /* Always trap */ +            generate_exception(ctx, EXCP_TRAP); +            break; +        case OPC_TLT:   /* rs < rs           */ +        case OPC_TLTI:  /* r0 < 0            */ +        case OPC_TLTU:  /* rs < rs unsigned  */ +        case OPC_TLTIU: /* r0 < 0  unsigned  */ +        case OPC_TNE:   /* rs != rs          */ +        case OPC_TNEI:  /* r0 != 0           */ +            /* Never trap: treat as NOP. */ +            break; +        } +    } else { +        TCGLabel *l1 = gen_new_label(); + +        switch (opc) { +        case OPC_TEQ: +        case OPC_TEQI: +            tcg_gen_brcond_tl(TCG_COND_NE, t0, t1, l1); +            break; +        case OPC_TGE: +        case OPC_TGEI: +            tcg_gen_brcond_tl(TCG_COND_LT, t0, t1, l1); +            break; +        case OPC_TGEU: +        case OPC_TGEIU: +            tcg_gen_brcond_tl(TCG_COND_LTU, t0, t1, l1); +            break; +        case OPC_TLT: +        case OPC_TLTI: +            tcg_gen_brcond_tl(TCG_COND_GE, t0, t1, l1); +            break; +        case OPC_TLTU: +        case OPC_TLTIU: +            tcg_gen_brcond_tl(TCG_COND_GEU, t0, t1, l1); +            break; +        case OPC_TNE: +        case OPC_TNEI: +            tcg_gen_brcond_tl(TCG_COND_EQ, t0, t1, l1); +            break; +        } +        generate_exception(ctx, EXCP_TRAP); +        gen_set_label(l1); +    } +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +static inline 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) && +        likely(!ctx->singlestep_enabled)) { +        tcg_gen_goto_tb(n); +        gen_save_pc(dest); +        tcg_gen_exit_tb((uintptr_t)tb + n); +    } else { +        gen_save_pc(dest); +        if (ctx->singlestep_enabled) { +            save_cpu_state(ctx, 0); +            gen_helper_0e0i(raise_exception, EXCP_DEBUG); +        } +        tcg_gen_exit_tb(0); +    } +} + +/* Branches (before delay slot) */ +static void gen_compute_branch (DisasContext *ctx, uint32_t opc, +                                int insn_bytes, +                                int rs, int rt, int32_t offset, +                                int delayslot_size) +{ +    target_ulong btgt = -1; +    int blink = 0; +    int bcond_compute = 0; +    TCGv t0 = tcg_temp_new(); +    TCGv t1 = tcg_temp_new(); + +    if (ctx->hflags & MIPS_HFLAG_BMASK) { +#ifdef MIPS_DEBUG_DISAS +        LOG_DISAS("Branch in delay / forbidden slot at PC 0x" +                  TARGET_FMT_lx "\n", ctx->pc); +#endif +        generate_exception(ctx, EXCP_RI); +        goto out; +    } + +    /* Load needed operands */ +    switch (opc) { +    case OPC_BEQ: +    case OPC_BEQL: +    case OPC_BNE: +    case OPC_BNEL: +        /* Compare two registers */ +        if (rs != rt) { +            gen_load_gpr(t0, rs); +            gen_load_gpr(t1, rt); +            bcond_compute = 1; +        } +        btgt = ctx->pc + insn_bytes + offset; +        break; +    case OPC_BGEZ: +    case OPC_BGEZAL: +    case OPC_BGEZALL: +    case OPC_BGEZL: +    case OPC_BGTZ: +    case OPC_BGTZL: +    case OPC_BLEZ: +    case OPC_BLEZL: +    case OPC_BLTZ: +    case OPC_BLTZAL: +    case OPC_BLTZALL: +    case OPC_BLTZL: +        /* Compare to zero */ +        if (rs != 0) { +            gen_load_gpr(t0, rs); +            bcond_compute = 1; +        } +        btgt = ctx->pc + insn_bytes + offset; +        break; +    case OPC_BPOSGE32: +#if defined(TARGET_MIPS64) +    case OPC_BPOSGE64: +        tcg_gen_andi_tl(t0, cpu_dspctrl, 0x7F); +#else +        tcg_gen_andi_tl(t0, cpu_dspctrl, 0x3F); +#endif +        bcond_compute = 1; +        btgt = ctx->pc + insn_bytes + offset; +        break; +    case OPC_J: +    case OPC_JAL: +    case OPC_JALX: +        /* Jump to immediate */ +        btgt = ((ctx->pc + insn_bytes) & (int32_t)0xF0000000) | (uint32_t)offset; +        break; +    case OPC_JR: +    case OPC_JALR: +        /* Jump to register */ +        if (offset != 0 && offset != 16) { +            /* Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the +               others are reserved. */ +            MIPS_INVAL("jump hint"); +            generate_exception(ctx, EXCP_RI); +            goto out; +        } +        gen_load_gpr(btarget, rs); +        break; +    default: +        MIPS_INVAL("branch/jump"); +        generate_exception(ctx, EXCP_RI); +        goto out; +    } +    if (bcond_compute == 0) { +        /* No condition to be computed */ +        switch (opc) { +        case OPC_BEQ:     /* rx == rx        */ +        case OPC_BEQL:    /* rx == rx likely */ +        case OPC_BGEZ:    /* 0 >= 0          */ +        case OPC_BGEZL:   /* 0 >= 0 likely   */ +        case OPC_BLEZ:    /* 0 <= 0          */ +        case OPC_BLEZL:   /* 0 <= 0 likely   */ +            /* Always take */ +            ctx->hflags |= MIPS_HFLAG_B; +            MIPS_DEBUG("balways"); +            break; +        case OPC_BGEZAL:  /* 0 >= 0          */ +        case OPC_BGEZALL: /* 0 >= 0 likely   */ +            /* Always take and link */ +            blink = 31; +            ctx->hflags |= MIPS_HFLAG_B; +            MIPS_DEBUG("balways and link"); +            break; +        case OPC_BNE:     /* rx != rx        */ +        case OPC_BGTZ:    /* 0 > 0           */ +        case OPC_BLTZ:    /* 0 < 0           */ +            /* Treat as NOP. */ +            MIPS_DEBUG("bnever (NOP)"); +            goto out; +        case OPC_BLTZAL:  /* 0 < 0           */ +            /* Handle as an unconditional branch to get correct delay +               slot checking.  */ +            blink = 31; +            btgt = ctx->pc + insn_bytes + delayslot_size; +            ctx->hflags |= MIPS_HFLAG_B; +            MIPS_DEBUG("bnever and link"); +            break; +        case OPC_BLTZALL: /* 0 < 0 likely */ +            tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 8); +            /* Skip the instruction in the delay slot */ +            MIPS_DEBUG("bnever, link and skip"); +            ctx->pc += 4; +            goto out; +        case OPC_BNEL:    /* rx != rx likely */ +        case OPC_BGTZL:   /* 0 > 0 likely */ +        case OPC_BLTZL:   /* 0 < 0 likely */ +            /* Skip the instruction in the delay slot */ +            MIPS_DEBUG("bnever and skip"); +            ctx->pc += 4; +            goto out; +        case OPC_J: +            ctx->hflags |= MIPS_HFLAG_B; +            MIPS_DEBUG("j " TARGET_FMT_lx, btgt); +            break; +        case OPC_JALX: +            ctx->hflags |= MIPS_HFLAG_BX; +            /* Fallthrough */ +        case OPC_JAL: +            blink = 31; +            ctx->hflags |= MIPS_HFLAG_B; +            MIPS_DEBUG("jal " TARGET_FMT_lx, btgt); +            break; +        case OPC_JR: +            ctx->hflags |= MIPS_HFLAG_BR; +            MIPS_DEBUG("jr %s", regnames[rs]); +            break; +        case OPC_JALR: +            blink = rt; +            ctx->hflags |= MIPS_HFLAG_BR; +            MIPS_DEBUG("jalr %s, %s", regnames[rt], regnames[rs]); +            break; +        default: +            MIPS_INVAL("branch/jump"); +            generate_exception(ctx, EXCP_RI); +            goto out; +        } +    } else { +        switch (opc) { +        case OPC_BEQ: +            tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1); +            MIPS_DEBUG("beq %s, %s, " TARGET_FMT_lx, +                       regnames[rs], regnames[rt], btgt); +            goto not_likely; +        case OPC_BEQL: +            tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1); +            MIPS_DEBUG("beql %s, %s, " TARGET_FMT_lx, +                       regnames[rs], regnames[rt], btgt); +            goto likely; +        case OPC_BNE: +            tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1); +            MIPS_DEBUG("bne %s, %s, " TARGET_FMT_lx, +                       regnames[rs], regnames[rt], btgt); +            goto not_likely; +        case OPC_BNEL: +            tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1); +            MIPS_DEBUG("bnel %s, %s, " TARGET_FMT_lx, +                       regnames[rs], regnames[rt], btgt); +            goto likely; +        case OPC_BGEZ: +            tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); +            MIPS_DEBUG("bgez %s, " TARGET_FMT_lx, regnames[rs], btgt); +            goto not_likely; +        case OPC_BGEZL: +            tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); +            MIPS_DEBUG("bgezl %s, " TARGET_FMT_lx, regnames[rs], btgt); +            goto likely; +        case OPC_BGEZAL: +            tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); +            MIPS_DEBUG("bgezal %s, " TARGET_FMT_lx, regnames[rs], btgt); +            blink = 31; +            goto not_likely; +        case OPC_BGEZALL: +            tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); +            blink = 31; +            MIPS_DEBUG("bgezall %s, " TARGET_FMT_lx, regnames[rs], btgt); +            goto likely; +        case OPC_BGTZ: +            tcg_gen_setcondi_tl(TCG_COND_GT, bcond, t0, 0); +            MIPS_DEBUG("bgtz %s, " TARGET_FMT_lx, regnames[rs], btgt); +            goto not_likely; +        case OPC_BGTZL: +            tcg_gen_setcondi_tl(TCG_COND_GT, bcond, t0, 0); +            MIPS_DEBUG("bgtzl %s, " TARGET_FMT_lx, regnames[rs], btgt); +            goto likely; +        case OPC_BLEZ: +            tcg_gen_setcondi_tl(TCG_COND_LE, bcond, t0, 0); +            MIPS_DEBUG("blez %s, " TARGET_FMT_lx, regnames[rs], btgt); +            goto not_likely; +        case OPC_BLEZL: +            tcg_gen_setcondi_tl(TCG_COND_LE, bcond, t0, 0); +            MIPS_DEBUG("blezl %s, " TARGET_FMT_lx, regnames[rs], btgt); +            goto likely; +        case OPC_BLTZ: +            tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); +            MIPS_DEBUG("bltz %s, " TARGET_FMT_lx, regnames[rs], btgt); +            goto not_likely; +        case OPC_BLTZL: +            tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); +            MIPS_DEBUG("bltzl %s, " TARGET_FMT_lx, regnames[rs], btgt); +            goto likely; +        case OPC_BPOSGE32: +            tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 32); +            MIPS_DEBUG("bposge32 " TARGET_FMT_lx, btgt); +            goto not_likely; +#if defined(TARGET_MIPS64) +        case OPC_BPOSGE64: +            tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 64); +            MIPS_DEBUG("bposge64 " TARGET_FMT_lx, btgt); +            goto not_likely; +#endif +        case OPC_BLTZAL: +            tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); +            blink = 31; +            MIPS_DEBUG("bltzal %s, " TARGET_FMT_lx, regnames[rs], btgt); +        not_likely: +            ctx->hflags |= MIPS_HFLAG_BC; +            break; +        case OPC_BLTZALL: +            tcg_gen_setcondi_tl(TCG_COND_LT, bcond, t0, 0); +            blink = 31; +            MIPS_DEBUG("bltzall %s, " TARGET_FMT_lx, regnames[rs], btgt); +        likely: +            ctx->hflags |= MIPS_HFLAG_BL; +            break; +        default: +            MIPS_INVAL("conditional branch/jump"); +            generate_exception(ctx, EXCP_RI); +            goto out; +        } +    } +    MIPS_DEBUG("enter ds: link %d cond %02x target " TARGET_FMT_lx, +               blink, ctx->hflags, btgt); + +    ctx->btarget = btgt; + +    switch (delayslot_size) { +    case 2: +        ctx->hflags |= MIPS_HFLAG_BDS16; +        break; +    case 4: +        ctx->hflags |= MIPS_HFLAG_BDS32; +        break; +    } + +    if (blink > 0) { +        int post_delay = insn_bytes + delayslot_size; +        int lowbit = !!(ctx->hflags & MIPS_HFLAG_M16); + +        tcg_gen_movi_tl(cpu_gpr[blink], ctx->pc + post_delay + lowbit); +    } + + out: +    if (insn_bytes == 2) +        ctx->hflags |= MIPS_HFLAG_B16; +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +/* special3 bitfield operations */ +static void gen_bitops (DisasContext *ctx, uint32_t opc, int rt, +                        int rs, int lsb, int msb) +{ +    TCGv t0 = tcg_temp_new(); +    TCGv t1 = tcg_temp_new(); + +    gen_load_gpr(t1, rs); +    switch (opc) { +    case OPC_EXT: +        if (lsb + msb > 31) { +            goto fail; +        } +        tcg_gen_shri_tl(t0, t1, lsb); +        if (msb != 31) { +            tcg_gen_andi_tl(t0, t0, (1U << (msb + 1)) - 1); +        } else { +            tcg_gen_ext32s_tl(t0, t0); +        } +        break; +#if defined(TARGET_MIPS64) +    case OPC_DEXTU: +        lsb += 32; +        goto do_dext; +    case OPC_DEXTM: +        msb += 32; +        goto do_dext; +    case OPC_DEXT: +    do_dext: +        if (lsb + msb > 63) { +            goto fail; +        } +        tcg_gen_shri_tl(t0, t1, lsb); +        if (msb != 63) { +            tcg_gen_andi_tl(t0, t0, (1ULL << (msb + 1)) - 1); +        } +        break; +#endif +    case OPC_INS: +        if (lsb > msb) { +            goto fail; +        } +        gen_load_gpr(t0, rt); +        tcg_gen_deposit_tl(t0, t0, t1, lsb, msb - lsb + 1); +        tcg_gen_ext32s_tl(t0, t0); +        break; +#if defined(TARGET_MIPS64) +    case OPC_DINSU: +        lsb += 32; +        /* FALLTHRU */ +    case OPC_DINSM: +        msb += 32; +        /* FALLTHRU */ +    case OPC_DINS: +        if (lsb > msb) { +            goto fail; +        } +        gen_load_gpr(t0, rt); +        tcg_gen_deposit_tl(t0, t0, t1, lsb, msb - lsb + 1); +        break; +#endif +    default: +fail: +        MIPS_INVAL("bitops"); +        generate_exception(ctx, EXCP_RI); +        tcg_temp_free(t0); +        tcg_temp_free(t1); +        return; +    } +    gen_store_gpr(t0, rt); +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +static void gen_bshfl (DisasContext *ctx, uint32_t op2, int rt, int rd) +{ +    TCGv t0; + +    if (rd == 0) { +        /* If no destination, treat it as a NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    gen_load_gpr(t0, rt); +    switch (op2) { +    case OPC_WSBH: +        { +            TCGv t1 = tcg_temp_new(); + +            tcg_gen_shri_tl(t1, t0, 8); +            tcg_gen_andi_tl(t1, t1, 0x00FF00FF); +            tcg_gen_shli_tl(t0, t0, 8); +            tcg_gen_andi_tl(t0, t0, ~0x00FF00FF); +            tcg_gen_or_tl(t0, t0, t1); +            tcg_temp_free(t1); +            tcg_gen_ext32s_tl(cpu_gpr[rd], t0); +        } +        break; +    case OPC_SEB: +        tcg_gen_ext8s_tl(cpu_gpr[rd], t0); +        break; +    case OPC_SEH: +        tcg_gen_ext16s_tl(cpu_gpr[rd], t0); +        break; +#if defined(TARGET_MIPS64) +    case OPC_DSBH: +        { +            TCGv t1 = tcg_temp_new(); + +            tcg_gen_shri_tl(t1, t0, 8); +            tcg_gen_andi_tl(t1, t1, 0x00FF00FF00FF00FFULL); +            tcg_gen_shli_tl(t0, t0, 8); +            tcg_gen_andi_tl(t0, t0, ~0x00FF00FF00FF00FFULL); +            tcg_gen_or_tl(cpu_gpr[rd], t0, t1); +            tcg_temp_free(t1); +        } +        break; +    case OPC_DSHD: +        { +            TCGv t1 = tcg_temp_new(); + +            tcg_gen_shri_tl(t1, t0, 16); +            tcg_gen_andi_tl(t1, t1, 0x0000FFFF0000FFFFULL); +            tcg_gen_shli_tl(t0, t0, 16); +            tcg_gen_andi_tl(t0, t0, ~0x0000FFFF0000FFFFULL); +            tcg_gen_or_tl(t0, t0, t1); +            tcg_gen_shri_tl(t1, t0, 32); +            tcg_gen_shli_tl(t0, t0, 32); +            tcg_gen_or_tl(cpu_gpr[rd], t0, t1); +            tcg_temp_free(t1); +        } +        break; +#endif +    default: +        MIPS_INVAL("bsfhl"); +        generate_exception(ctx, EXCP_RI); +        tcg_temp_free(t0); +        return; +    } +    tcg_temp_free(t0); +} + +static void gen_lsa(DisasContext *ctx, int opc, int rd, int rs, int rt, +                    int imm2) +{ +    TCGv t0; +    TCGv t1; +    if (rd == 0) { +        /* Treat as NOP. */ +        return; +    } +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); +    gen_load_gpr(t0, rs); +    gen_load_gpr(t1, rt); +    tcg_gen_shli_tl(t0, t0, imm2 + 1); +    tcg_gen_add_tl(cpu_gpr[rd], t0, t1); +    if (opc == OPC_LSA) { +        tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); +    } + +    tcg_temp_free(t1); +    tcg_temp_free(t0); + +    return; +} + +static void gen_align(DisasContext *ctx, int opc, int rd, int rs, int rt, +                      int bp) +{ +    TCGv t0; +    if (rd == 0) { +        /* Treat as NOP. */ +        return; +    } +    t0 = tcg_temp_new(); +    gen_load_gpr(t0, rt); +    if (bp == 0) { +        tcg_gen_mov_tl(cpu_gpr[rd], t0); +    } else { +        TCGv t1 = tcg_temp_new(); +        gen_load_gpr(t1, rs); +        switch (opc) { +        case OPC_ALIGN: +            { +                TCGv_i64 t2 = tcg_temp_new_i64(); +                tcg_gen_concat_tl_i64(t2, t1, t0); +                tcg_gen_shri_i64(t2, t2, 8 * (4 - bp)); +                gen_move_low32(cpu_gpr[rd], t2); +                tcg_temp_free_i64(t2); +            } +            break; +#if defined(TARGET_MIPS64) +        case OPC_DALIGN: +            tcg_gen_shli_tl(t0, t0, 8 * bp); +            tcg_gen_shri_tl(t1, t1, 8 * (8 - bp)); +            tcg_gen_or_tl(cpu_gpr[rd], t1, t0); +            break; +#endif +        } +        tcg_temp_free(t1); +    } + +    tcg_temp_free(t0); +} + +static void gen_bitswap(DisasContext *ctx, int opc, int rd, int rt) +{ +    TCGv t0; +    if (rd == 0) { +        /* Treat as NOP. */ +        return; +    } +    t0 = tcg_temp_new(); +    gen_load_gpr(t0, rt); +    switch (opc) { +    case OPC_BITSWAP: +        gen_helper_bitswap(cpu_gpr[rd], t0); +        break; +#if defined(TARGET_MIPS64) +    case OPC_DBITSWAP: +        gen_helper_dbitswap(cpu_gpr[rd], t0); +        break; +#endif +    } +    tcg_temp_free(t0); +} + +#ifndef CONFIG_USER_ONLY +/* CP0 (MMU and control) */ +static inline void gen_mthc0_entrylo(TCGv arg, target_ulong off) +{ +    TCGv_i64 t0 = tcg_temp_new_i64(); +    TCGv_i64 t1 = tcg_temp_new_i64(); + +    tcg_gen_ext_tl_i64(t0, arg); +    tcg_gen_ld_i64(t1, cpu_env, off); +#if defined(TARGET_MIPS64) +    tcg_gen_deposit_i64(t1, t1, t0, 30, 32); +#else +    tcg_gen_concat32_i64(t1, t1, t0); +#endif +    tcg_gen_st_i64(t1, cpu_env, off); +    tcg_temp_free_i64(t1); +    tcg_temp_free_i64(t0); +} + +static inline void gen_mthc0_store64(TCGv arg, target_ulong off) +{ +    TCGv_i64 t0 = tcg_temp_new_i64(); +    TCGv_i64 t1 = tcg_temp_new_i64(); + +    tcg_gen_ext_tl_i64(t0, arg); +    tcg_gen_ld_i64(t1, cpu_env, off); +    tcg_gen_concat32_i64(t1, t1, t0); +    tcg_gen_st_i64(t1, cpu_env, off); +    tcg_temp_free_i64(t1); +    tcg_temp_free_i64(t0); +} + +static inline void gen_mfhc0_entrylo(TCGv arg, target_ulong off) +{ +    TCGv_i64 t0 = tcg_temp_new_i64(); + +    tcg_gen_ld_i64(t0, cpu_env, off); +#if defined(TARGET_MIPS64) +    tcg_gen_shri_i64(t0, t0, 30); +#else +    tcg_gen_shri_i64(t0, t0, 32); +#endif +    gen_move_low32(arg, t0); +    tcg_temp_free_i64(t0); +} + +static inline void gen_mfhc0_load64(TCGv arg, target_ulong off, int shift) +{ +    TCGv_i64 t0 = tcg_temp_new_i64(); + +    tcg_gen_ld_i64(t0, cpu_env, off); +    tcg_gen_shri_i64(t0, t0, 32 + shift); +    gen_move_low32(arg, t0); +    tcg_temp_free_i64(t0); +} + +static inline void gen_mfc0_load32 (TCGv arg, target_ulong off) +{ +    TCGv_i32 t0 = tcg_temp_new_i32(); + +    tcg_gen_ld_i32(t0, cpu_env, off); +    tcg_gen_ext_i32_tl(arg, t0); +    tcg_temp_free_i32(t0); +} + +static inline void gen_mfc0_load64 (TCGv arg, target_ulong off) +{ +    tcg_gen_ld_tl(arg, cpu_env, off); +    tcg_gen_ext32s_tl(arg, arg); +} + +static inline void gen_mtc0_store32 (TCGv arg, target_ulong off) +{ +    TCGv_i32 t0 = tcg_temp_new_i32(); + +    tcg_gen_trunc_tl_i32(t0, arg); +    tcg_gen_st_i32(t0, cpu_env, off); +    tcg_temp_free_i32(t0); +} + +static inline void gen_mtc0_store64 (TCGv arg, target_ulong off) +{ +    tcg_gen_ext32s_tl(arg, arg); +    tcg_gen_st_tl(arg, cpu_env, off); +} + +static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) +{ +    const char *rn = "invalid"; + +    if (!(ctx->hflags & MIPS_HFLAG_ELPA)) { +        goto mfhc0_read_zero; +    } + +    switch (reg) { +    case 2: +        switch (sel) { +        case 0: +            gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0)); +            rn = "EntryLo0"; +            break; +        default: +            goto mfhc0_read_zero; +        } +        break; +    case 3: +        switch (sel) { +        case 0: +            gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1)); +            rn = "EntryLo1"; +            break; +        default: +            goto mfhc0_read_zero; +        } +        break; +    case 17: +        switch (sel) { +        case 0: +            gen_mfhc0_load64(arg, offsetof(CPUMIPSState, lladdr), +                             ctx->CP0_LLAddr_shift); +            rn = "LLAddr"; +            break; +        default: +            goto mfhc0_read_zero; +        } +        break; +    case 28: +        switch (sel) { +        case 0: +        case 2: +        case 4: +        case 6: +            gen_mfhc0_load64(arg, offsetof(CPUMIPSState, CP0_TagLo), 0); +            rn = "TagLo"; +            break; +        default: +            goto mfhc0_read_zero; +        } +        break; +    default: +        goto mfhc0_read_zero; +    } + +    (void)rn; /* avoid a compiler warning */ +    LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); +    return; + +mfhc0_read_zero: +    LOG_DISAS("mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); +    tcg_gen_movi_tl(arg, 0); +} + +static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) +{ +    const char *rn = "invalid"; +    uint64_t mask = ctx->PAMask >> 36; + +    if (!(ctx->hflags & MIPS_HFLAG_ELPA)) { +        goto mthc0_nop; +    } + +    switch (reg) { +    case 2: +        switch (sel) { +        case 0: +            tcg_gen_andi_tl(arg, arg, mask); +            gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0)); +            rn = "EntryLo0"; +            break; +        default: +            goto mthc0_nop; +        } +        break; +    case 3: +        switch (sel) { +        case 0: +            tcg_gen_andi_tl(arg, arg, mask); +            gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1)); +            rn = "EntryLo1"; +            break; +        default: +            goto mthc0_nop; +        } +        break; +    case 17: +        switch (sel) { +        case 0: +            /* LLAddr is read-only (the only exception is bit 0 if LLB is +               supported); the CP0_LLAddr_rw_bitmask does not seem to be +               relevant for modern MIPS cores supporting MTHC0, therefore +               treating MTHC0 to LLAddr as NOP. */ +            rn = "LLAddr"; +            break; +        default: +            goto mthc0_nop; +        } +        break; +    case 28: +        switch (sel) { +        case 0: +        case 2: +        case 4: +        case 6: +            tcg_gen_andi_tl(arg, arg, mask); +            gen_mthc0_store64(arg, offsetof(CPUMIPSState, CP0_TagLo)); +            rn = "TagLo"; +            break; +        default: +            goto mthc0_nop; +        } +        break; +    default: +        goto mthc0_nop; +    } + +    (void)rn; /* avoid a compiler warning */ +mthc0_nop: +    LOG_DISAS("mthc0 %s (reg %d sel %d)\n", rn, reg, sel); +} + +static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg) +{ +    if (ctx->insn_flags & ISA_MIPS32R6) { +        tcg_gen_movi_tl(arg, 0); +    } else { +        tcg_gen_movi_tl(arg, ~0); +    } +} + +#define CP0_CHECK(c)                            \ +    do {                                        \ +        if (!(c)) {                             \ +            goto cp0_unimplemented;             \ +        }                                       \ +    } while (0) + +static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) +{ +    const char *rn = "invalid"; + +    if (sel != 0) +        check_insn(ctx, ISA_MIPS32); + +    switch (reg) { +    case 0: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Index)); +            rn = "Index"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_mvpcontrol(arg, cpu_env); +            rn = "MVPControl"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_mvpconf0(arg, cpu_env); +            rn = "MVPConf0"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_mvpconf1(arg, cpu_env); +            rn = "MVPConf1"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 1: +        switch (sel) { +        case 0: +            CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); +            gen_helper_mfc0_random(arg, cpu_env); +            rn = "Random"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); +            rn = "VPEControl"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); +            rn = "VPEConf0"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); +            rn = "VPEConf1"; +            break; +        case 4: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_YQMask)); +            rn = "YQMask"; +            break; +        case 5: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); +            rn = "VPESchedule"; +            break; +        case 6: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); +            rn = "VPEScheFBack"; +            break; +        case 7: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); +            rn = "VPEOpt"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 2: +        switch (sel) { +        case 0: +            { +                TCGv_i64 tmp = tcg_temp_new_i64(); +                tcg_gen_ld_i64(tmp, cpu_env, +                               offsetof(CPUMIPSState, CP0_EntryLo0)); +#if defined(TARGET_MIPS64) +                if (ctx->rxi) { +                    /* Move RI/XI fields to bits 31:30 */ +                    tcg_gen_shri_tl(arg, tmp, CP0EnLo_XI); +                    tcg_gen_deposit_tl(tmp, tmp, arg, 30, 2); +                } +#endif +                gen_move_low32(arg, tmp); +                tcg_temp_free_i64(tmp); +            } +            rn = "EntryLo0"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_tcstatus(arg, cpu_env); +            rn = "TCStatus"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_tcbind(arg, cpu_env); +            rn = "TCBind"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_tcrestart(arg, cpu_env); +            rn = "TCRestart"; +            break; +        case 4: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_tchalt(arg, cpu_env); +            rn = "TCHalt"; +            break; +        case 5: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_tccontext(arg, cpu_env); +            rn = "TCContext"; +            break; +        case 6: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_tcschedule(arg, cpu_env); +            rn = "TCSchedule"; +            break; +        case 7: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_tcschefback(arg, cpu_env); +            rn = "TCScheFBack"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 3: +        switch (sel) { +        case 0: +            { +                TCGv_i64 tmp = tcg_temp_new_i64(); +                tcg_gen_ld_i64(tmp, cpu_env, +                               offsetof(CPUMIPSState, CP0_EntryLo1)); +#if defined(TARGET_MIPS64) +                if (ctx->rxi) { +                    /* Move RI/XI fields to bits 31:30 */ +                    tcg_gen_shri_tl(arg, tmp, CP0EnLo_XI); +                    tcg_gen_deposit_tl(tmp, tmp, arg, 30, 2); +                } +#endif +                gen_move_low32(arg, tmp); +                tcg_temp_free_i64(tmp); +            } +            rn = "EntryLo1"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 4: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_Context)); +            tcg_gen_ext32s_tl(arg, arg); +            rn = "Context"; +            break; +        case 1: +//            gen_helper_mfc0_contextconfig(arg); /* SmartMIPS ASE */ +            rn = "ContextConfig"; +            goto cp0_unimplemented; +//            break; +        case 2: +            CP0_CHECK(ctx->ulri); +            tcg_gen_ld32s_tl(arg, cpu_env, +                             offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); +            rn = "UserLocal"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 5: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageMask)); +            rn = "PageMask"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageGrain)); +            rn = "PageGrain"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 6: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Wired)); +            rn = "Wired"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf0)); +            rn = "SRSConf0"; +            break; +        case 2: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf1)); +            rn = "SRSConf1"; +            break; +        case 3: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf2)); +            rn = "SRSConf2"; +            break; +        case 4: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf3)); +            rn = "SRSConf3"; +            break; +        case 5: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf4)); +            rn = "SRSConf4"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 7: +        switch (sel) { +        case 0: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_HWREna)); +            rn = "HWREna"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 8: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); +            tcg_gen_ext32s_tl(arg, arg); +            rn = "BadVAddr"; +            break; +        case 1: +            CP0_CHECK(ctx->bi); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstr)); +            rn = "BadInstr"; +            break; +        case 2: +            CP0_CHECK(ctx->bp); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrP)); +            rn = "BadInstrP"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 9: +        switch (sel) { +        case 0: +            /* Mark as an IO operation because we read the time.  */ +            if (ctx->tb->cflags & CF_USE_ICOUNT) { +                gen_io_start(); +	    } +            gen_helper_mfc0_count(arg, cpu_env); +            if (ctx->tb->cflags & CF_USE_ICOUNT) { +                gen_io_end(); +            } +            /* Break the TB to be able to take timer interrupts immediately +               after reading count.  */ +            ctx->bstate = BS_STOP; +            rn = "Count"; +            break; +        /* 6,7 are implementation dependent */ +        default: +            goto cp0_unimplemented; +        } +        break; +    case 10: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryHi)); +            tcg_gen_ext32s_tl(arg, arg); +            rn = "EntryHi"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 11: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Compare)); +            rn = "Compare"; +            break; +        /* 6,7 are implementation dependent */ +        default: +            goto cp0_unimplemented; +        } +        break; +    case 12: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Status)); +            rn = "Status"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_IntCtl)); +            rn = "IntCtl"; +            break; +        case 2: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSCtl)); +            rn = "SRSCtl"; +            break; +        case 3: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); +            rn = "SRSMap"; +            break; +        default: +            goto cp0_unimplemented; +       } +        break; +    case 13: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Cause)); +            rn = "Cause"; +            break; +        default: +            goto cp0_unimplemented; +       } +        break; +    case 14: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); +            tcg_gen_ext32s_tl(arg, arg); +            rn = "EPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 15: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PRid)); +            rn = "PRid"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase)); +            rn = "EBase"; +            break; +        default: +            goto cp0_unimplemented; +       } +        break; +    case 16: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config0)); +            rn = "Config"; +            break; +        case 1: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config1)); +            rn = "Config1"; +            break; +        case 2: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config2)); +            rn = "Config2"; +            break; +        case 3: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config3)); +            rn = "Config3"; +            break; +        case 4: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config4)); +            rn = "Config4"; +            break; +        case 5: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config5)); +            rn = "Config5"; +            break; +        /* 6,7 are implementation dependent */ +        case 6: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config6)); +            rn = "Config6"; +            break; +        case 7: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config7)); +            rn = "Config7"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 17: +        switch (sel) { +        case 0: +            gen_helper_mfc0_lladdr(arg, cpu_env); +            rn = "LLAddr"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 18: +        switch (sel) { +        case 0 ... 7: +            gen_helper_1e0i(mfc0_watchlo, arg, sel); +            rn = "WatchLo"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 19: +        switch (sel) { +        case 0 ...7: +            gen_helper_1e0i(mfc0_watchhi, arg, sel); +            rn = "WatchHi"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 20: +        switch (sel) { +        case 0: +#if defined(TARGET_MIPS64) +            check_insn(ctx, ISA_MIPS3); +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext)); +            tcg_gen_ext32s_tl(arg, arg); +            rn = "XContext"; +            break; +#endif +        default: +            goto cp0_unimplemented; +        } +        break; +    case 21: +       /* Officially reserved, but sel 0 is used for R1x000 framemask */ +        CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Framemask)); +            rn = "Framemask"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 22: +        tcg_gen_movi_tl(arg, 0); /* unimplemented */ +        rn = "'Diagnostic"; /* implementation dependent */ +        break; +    case 23: +        switch (sel) { +        case 0: +            gen_helper_mfc0_debug(arg, cpu_env); /* EJTAG support */ +            rn = "Debug"; +            break; +        case 1: +//            gen_helper_mfc0_tracecontrol(arg); /* PDtrace support */ +            rn = "TraceControl"; +//            break; +        case 2: +//            gen_helper_mfc0_tracecontrol2(arg); /* PDtrace support */ +            rn = "TraceControl2"; +//            break; +        case 3: +//            gen_helper_mfc0_usertracedata(arg); /* PDtrace support */ +            rn = "UserTraceData"; +//            break; +        case 4: +//            gen_helper_mfc0_tracebpc(arg); /* PDtrace support */ +            rn = "TraceBPC"; +//            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 24: +        switch (sel) { +        case 0: +            /* EJTAG support */ +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); +            tcg_gen_ext32s_tl(arg, arg); +            rn = "DEPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 25: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Performance0)); +            rn = "Performance0"; +            break; +        case 1: +//            gen_helper_mfc0_performance1(arg); +            rn = "Performance1"; +//            break; +        case 2: +//            gen_helper_mfc0_performance2(arg); +            rn = "Performance2"; +//            break; +        case 3: +//            gen_helper_mfc0_performance3(arg); +            rn = "Performance3"; +//            break; +        case 4: +//            gen_helper_mfc0_performance4(arg); +            rn = "Performance4"; +//            break; +        case 5: +//            gen_helper_mfc0_performance5(arg); +            rn = "Performance5"; +//            break; +        case 6: +//            gen_helper_mfc0_performance6(arg); +            rn = "Performance6"; +//            break; +        case 7: +//            gen_helper_mfc0_performance7(arg); +            rn = "Performance7"; +//            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 26: +        tcg_gen_movi_tl(arg, 0); /* unimplemented */ +        rn = "ECC"; +        break; +    case 27: +        switch (sel) { +        case 0 ... 3: +            tcg_gen_movi_tl(arg, 0); /* unimplemented */ +            rn = "CacheErr"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 28: +        switch (sel) { +        case 0: +        case 2: +        case 4: +        case 6: +            { +                TCGv_i64 tmp = tcg_temp_new_i64(); +                tcg_gen_ld_i64(tmp, cpu_env, offsetof(CPUMIPSState, CP0_TagLo)); +                gen_move_low32(arg, tmp); +                tcg_temp_free_i64(tmp); +            } +            rn = "TagLo"; +            break; +        case 1: +        case 3: +        case 5: +        case 7: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataLo)); +            rn = "DataLo"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 29: +        switch (sel) { +        case 0: +        case 2: +        case 4: +        case 6: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagHi)); +            rn = "TagHi"; +            break; +        case 1: +        case 3: +        case 5: +        case 7: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataHi)); +            rn = "DataHi"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 30: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); +            tcg_gen_ext32s_tl(arg, arg); +            rn = "ErrorEPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 31: +        switch (sel) { +        case 0: +            /* EJTAG support */ +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); +            rn = "DESAVE"; +            break; +        case 2 ... 7: +            CP0_CHECK(ctx->kscrexist & (1 << sel)); +            tcg_gen_ld_tl(arg, cpu_env, +                          offsetof(CPUMIPSState, CP0_KScratch[sel-2])); +            tcg_gen_ext32s_tl(arg, arg); +            rn = "KScratch"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    default: +       goto cp0_unimplemented; +    } +    (void)rn; /* avoid a compiler warning */ +    LOG_DISAS("mfc0 %s (reg %d sel %d)\n", rn, reg, sel); +    return; + +cp0_unimplemented: +    LOG_DISAS("mfc0 %s (reg %d sel %d)\n", rn, reg, sel); +    gen_mfc0_unimplemented(ctx, arg); +} + +static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) +{ +    const char *rn = "invalid"; + +    if (sel != 0) +        check_insn(ctx, ISA_MIPS32); + +    if (ctx->tb->cflags & CF_USE_ICOUNT) { +        gen_io_start(); +    } + +    switch (reg) { +    case 0: +        switch (sel) { +        case 0: +            gen_helper_mtc0_index(cpu_env, arg); +            rn = "Index"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_mvpcontrol(cpu_env, arg); +            rn = "MVPControl"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            /* ignored */ +            rn = "MVPConf0"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            /* ignored */ +            rn = "MVPConf1"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 1: +        switch (sel) { +        case 0: +            /* ignored */ +            rn = "Random"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_vpecontrol(cpu_env, arg); +            rn = "VPEControl"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_vpeconf0(cpu_env, arg); +            rn = "VPEConf0"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_vpeconf1(cpu_env, arg); +            rn = "VPEConf1"; +            break; +        case 4: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_yqmask(cpu_env, arg); +            rn = "YQMask"; +            break; +        case 5: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); +            rn = "VPESchedule"; +            break; +        case 6: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); +            rn = "VPEScheFBack"; +            break; +        case 7: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_vpeopt(cpu_env, arg); +            rn = "VPEOpt"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 2: +        switch (sel) { +        case 0: +            gen_helper_mtc0_entrylo0(cpu_env, arg); +            rn = "EntryLo0"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tcstatus(cpu_env, arg); +            rn = "TCStatus"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tcbind(cpu_env, arg); +            rn = "TCBind"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tcrestart(cpu_env, arg); +            rn = "TCRestart"; +            break; +        case 4: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tchalt(cpu_env, arg); +            rn = "TCHalt"; +            break; +        case 5: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tccontext(cpu_env, arg); +            rn = "TCContext"; +            break; +        case 6: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tcschedule(cpu_env, arg); +            rn = "TCSchedule"; +            break; +        case 7: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tcschefback(cpu_env, arg); +            rn = "TCScheFBack"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 3: +        switch (sel) { +        case 0: +            gen_helper_mtc0_entrylo1(cpu_env, arg); +            rn = "EntryLo1"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 4: +        switch (sel) { +        case 0: +            gen_helper_mtc0_context(cpu_env, arg); +            rn = "Context"; +            break; +        case 1: +//            gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */ +            rn = "ContextConfig"; +            goto cp0_unimplemented; +//            break; +        case 2: +            CP0_CHECK(ctx->ulri); +            tcg_gen_st_tl(arg, cpu_env, +                          offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); +            rn = "UserLocal"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 5: +        switch (sel) { +        case 0: +            gen_helper_mtc0_pagemask(cpu_env, arg); +            rn = "PageMask"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_pagegrain(cpu_env, arg); +            rn = "PageGrain"; +            ctx->bstate = BS_STOP; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 6: +        switch (sel) { +        case 0: +            gen_helper_mtc0_wired(cpu_env, arg); +            rn = "Wired"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsconf0(cpu_env, arg); +            rn = "SRSConf0"; +            break; +        case 2: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsconf1(cpu_env, arg); +            rn = "SRSConf1"; +            break; +        case 3: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsconf2(cpu_env, arg); +            rn = "SRSConf2"; +            break; +        case 4: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsconf3(cpu_env, arg); +            rn = "SRSConf3"; +            break; +        case 5: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsconf4(cpu_env, arg); +            rn = "SRSConf4"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 7: +        switch (sel) { +        case 0: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_hwrena(cpu_env, arg); +            ctx->bstate = BS_STOP; +            rn = "HWREna"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 8: +        switch (sel) { +        case 0: +            /* ignored */ +            rn = "BadVAddr"; +            break; +        case 1: +            /* ignored */ +            rn = "BadInstr"; +            break; +        case 2: +            /* ignored */ +            rn = "BadInstrP"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 9: +        switch (sel) { +        case 0: +            gen_helper_mtc0_count(cpu_env, arg); +            rn = "Count"; +            break; +        /* 6,7 are implementation dependent */ +        default: +            goto cp0_unimplemented; +        } +        break; +    case 10: +        switch (sel) { +        case 0: +            gen_helper_mtc0_entryhi(cpu_env, arg); +            rn = "EntryHi"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 11: +        switch (sel) { +        case 0: +            gen_helper_mtc0_compare(cpu_env, arg); +            rn = "Compare"; +            break; +        /* 6,7 are implementation dependent */ +        default: +            goto cp0_unimplemented; +        } +        break; +    case 12: +        switch (sel) { +        case 0: +            save_cpu_state(ctx, 1); +            gen_helper_mtc0_status(cpu_env, arg); +            /* BS_STOP isn't good enough here, hflags may have changed. */ +            gen_save_pc(ctx->pc + 4); +            ctx->bstate = BS_EXCP; +            rn = "Status"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_intctl(cpu_env, arg); +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "IntCtl"; +            break; +        case 2: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsctl(cpu_env, arg); +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "SRSCtl"; +            break; +        case 3: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "SRSMap"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 13: +        switch (sel) { +        case 0: +            save_cpu_state(ctx, 1); +            gen_helper_mtc0_cause(cpu_env, arg); +            rn = "Cause"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 14: +        switch (sel) { +        case 0: +            gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_EPC)); +            rn = "EPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 15: +        switch (sel) { +        case 0: +            /* ignored */ +            rn = "PRid"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_ebase(cpu_env, arg); +            rn = "EBase"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 16: +        switch (sel) { +        case 0: +            gen_helper_mtc0_config0(cpu_env, arg); +            rn = "Config"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            break; +        case 1: +            /* ignored, read only */ +            rn = "Config1"; +            break; +        case 2: +            gen_helper_mtc0_config2(cpu_env, arg); +            rn = "Config2"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            break; +        case 3: +            gen_helper_mtc0_config3(cpu_env, arg); +            rn = "Config3"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            break; +        case 4: +            gen_helper_mtc0_config4(cpu_env, arg); +            rn = "Config4"; +            ctx->bstate = BS_STOP; +            break; +        case 5: +            gen_helper_mtc0_config5(cpu_env, arg); +            rn = "Config5"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            break; +        /* 6,7 are implementation dependent */ +        case 6: +            /* ignored */ +            rn = "Config6"; +            break; +        case 7: +            /* ignored */ +            rn = "Config7"; +            break; +        default: +            rn = "Invalid config selector"; +            goto cp0_unimplemented; +        } +        break; +    case 17: +        switch (sel) { +        case 0: +            gen_helper_mtc0_lladdr(cpu_env, arg); +            rn = "LLAddr"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 18: +        switch (sel) { +        case 0 ... 7: +            gen_helper_0e1i(mtc0_watchlo, arg, sel); +            rn = "WatchLo"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 19: +        switch (sel) { +        case 0 ... 7: +            gen_helper_0e1i(mtc0_watchhi, arg, sel); +            rn = "WatchHi"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 20: +        switch (sel) { +        case 0: +#if defined(TARGET_MIPS64) +            check_insn(ctx, ISA_MIPS3); +            gen_helper_mtc0_xcontext(cpu_env, arg); +            rn = "XContext"; +            break; +#endif +        default: +            goto cp0_unimplemented; +        } +        break; +    case 21: +       /* Officially reserved, but sel 0 is used for R1x000 framemask */ +        CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); +        switch (sel) { +        case 0: +            gen_helper_mtc0_framemask(cpu_env, arg); +            rn = "Framemask"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 22: +        /* ignored */ +        rn = "Diagnostic"; /* implementation dependent */ +        break; +    case 23: +        switch (sel) { +        case 0: +            gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ +            /* BS_STOP isn't good enough here, hflags may have changed. */ +            gen_save_pc(ctx->pc + 4); +            ctx->bstate = BS_EXCP; +            rn = "Debug"; +            break; +        case 1: +//            gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */ +            rn = "TraceControl"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +//            break; +        case 2: +//            gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */ +            rn = "TraceControl2"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +//            break; +        case 3: +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +//            gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */ +            rn = "UserTraceData"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +//            break; +        case 4: +//            gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */ +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "TraceBPC"; +//            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 24: +        switch (sel) { +        case 0: +            /* EJTAG support */ +            gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_DEPC)); +            rn = "DEPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 25: +        switch (sel) { +        case 0: +            gen_helper_mtc0_performance0(cpu_env, arg); +            rn = "Performance0"; +            break; +        case 1: +//            gen_helper_mtc0_performance1(arg); +            rn = "Performance1"; +//            break; +        case 2: +//            gen_helper_mtc0_performance2(arg); +            rn = "Performance2"; +//            break; +        case 3: +//            gen_helper_mtc0_performance3(arg); +            rn = "Performance3"; +//            break; +        case 4: +//            gen_helper_mtc0_performance4(arg); +            rn = "Performance4"; +//            break; +        case 5: +//            gen_helper_mtc0_performance5(arg); +            rn = "Performance5"; +//            break; +        case 6: +//            gen_helper_mtc0_performance6(arg); +            rn = "Performance6"; +//            break; +        case 7: +//            gen_helper_mtc0_performance7(arg); +            rn = "Performance7"; +//            break; +        default: +            goto cp0_unimplemented; +        } +       break; +    case 26: +        /* ignored */ +        rn = "ECC"; +        break; +    case 27: +        switch (sel) { +        case 0 ... 3: +            /* ignored */ +            rn = "CacheErr"; +            break; +        default: +            goto cp0_unimplemented; +        } +       break; +    case 28: +        switch (sel) { +        case 0: +        case 2: +        case 4: +        case 6: +            gen_helper_mtc0_taglo(cpu_env, arg); +            rn = "TagLo"; +            break; +        case 1: +        case 3: +        case 5: +        case 7: +            gen_helper_mtc0_datalo(cpu_env, arg); +            rn = "DataLo"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 29: +        switch (sel) { +        case 0: +        case 2: +        case 4: +        case 6: +            gen_helper_mtc0_taghi(cpu_env, arg); +            rn = "TagHi"; +            break; +        case 1: +        case 3: +        case 5: +        case 7: +            gen_helper_mtc0_datahi(cpu_env, arg); +            rn = "DataHi"; +            break; +        default: +            rn = "invalid sel"; +            goto cp0_unimplemented; +        } +       break; +    case 30: +        switch (sel) { +        case 0: +            gen_mtc0_store64(arg, offsetof(CPUMIPSState, CP0_ErrorEPC)); +            rn = "ErrorEPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 31: +        switch (sel) { +        case 0: +            /* EJTAG support */ +            gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); +            rn = "DESAVE"; +            break; +        case 2 ... 7: +            CP0_CHECK(ctx->kscrexist & (1 << sel)); +            tcg_gen_st_tl(arg, cpu_env, +                          offsetof(CPUMIPSState, CP0_KScratch[sel-2])); +            rn = "KScratch"; +            break; +        default: +            goto cp0_unimplemented; +        } +        /* Stop translation as we may have switched the execution mode */ +        ctx->bstate = BS_STOP; +        break; +    default: +       goto cp0_unimplemented; +    } +    (void)rn; /* avoid a compiler warning */ +    LOG_DISAS("mtc0 %s (reg %d sel %d)\n", rn, reg, sel); +    /* For simplicity assume that all writes can cause interrupts.  */ +    if (ctx->tb->cflags & CF_USE_ICOUNT) { +        gen_io_end(); +        ctx->bstate = BS_STOP; +    } +    return; + +cp0_unimplemented: +    LOG_DISAS("mtc0 %s (reg %d sel %d)\n", rn, reg, sel); +} + +#if defined(TARGET_MIPS64) +static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) +{ +    const char *rn = "invalid"; + +    if (sel != 0) +        check_insn(ctx, ISA_MIPS64); + +    switch (reg) { +    case 0: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Index)); +            rn = "Index"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_mvpcontrol(arg, cpu_env); +            rn = "MVPControl"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_mvpconf0(arg, cpu_env); +            rn = "MVPConf0"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_mvpconf1(arg, cpu_env); +            rn = "MVPConf1"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 1: +        switch (sel) { +        case 0: +            CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); +            gen_helper_mfc0_random(arg, cpu_env); +            rn = "Random"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); +            rn = "VPEControl"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); +            rn = "VPEConf0"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); +            rn = "VPEConf1"; +            break; +        case 4: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_YQMask)); +            rn = "YQMask"; +            break; +        case 5: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); +            rn = "VPESchedule"; +            break; +        case 6: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); +            rn = "VPEScheFBack"; +            break; +        case 7: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); +            rn = "VPEOpt"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 2: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo0)); +            rn = "EntryLo0"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_tcstatus(arg, cpu_env); +            rn = "TCStatus"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mfc0_tcbind(arg, cpu_env); +            rn = "TCBind"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_dmfc0_tcrestart(arg, cpu_env); +            rn = "TCRestart"; +            break; +        case 4: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_dmfc0_tchalt(arg, cpu_env); +            rn = "TCHalt"; +            break; +        case 5: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_dmfc0_tccontext(arg, cpu_env); +            rn = "TCContext"; +            break; +        case 6: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_dmfc0_tcschedule(arg, cpu_env); +            rn = "TCSchedule"; +            break; +        case 7: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_dmfc0_tcschefback(arg, cpu_env); +            rn = "TCScheFBack"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 3: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1)); +            rn = "EntryLo1"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 4: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_Context)); +            rn = "Context"; +            break; +        case 1: +//            gen_helper_dmfc0_contextconfig(arg); /* SmartMIPS ASE */ +            rn = "ContextConfig"; +            goto cp0_unimplemented; +//            break; +        case 2: +            CP0_CHECK(ctx->ulri); +            tcg_gen_ld_tl(arg, cpu_env, +                          offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); +            rn = "UserLocal"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 5: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageMask)); +            rn = "PageMask"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageGrain)); +            rn = "PageGrain"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 6: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Wired)); +            rn = "Wired"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf0)); +            rn = "SRSConf0"; +            break; +        case 2: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf1)); +            rn = "SRSConf1"; +            break; +        case 3: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf2)); +            rn = "SRSConf2"; +            break; +        case 4: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf3)); +            rn = "SRSConf3"; +            break; +        case 5: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf4)); +            rn = "SRSConf4"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 7: +        switch (sel) { +        case 0: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_HWREna)); +            rn = "HWREna"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 8: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); +            rn = "BadVAddr"; +            break; +        case 1: +            CP0_CHECK(ctx->bi); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstr)); +            rn = "BadInstr"; +            break; +        case 2: +            CP0_CHECK(ctx->bp); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrP)); +            rn = "BadInstrP"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 9: +        switch (sel) { +        case 0: +            /* Mark as an IO operation because we read the time.  */ +            if (ctx->tb->cflags & CF_USE_ICOUNT) { +                gen_io_start(); +            } +            gen_helper_mfc0_count(arg, cpu_env); +            if (ctx->tb->cflags & CF_USE_ICOUNT) { +                gen_io_end(); +            } +            /* Break the TB to be able to take timer interrupts immediately +               after reading count.  */ +            ctx->bstate = BS_STOP; +            rn = "Count"; +            break; +        /* 6,7 are implementation dependent */ +        default: +            goto cp0_unimplemented; +        } +        break; +    case 10: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryHi)); +            rn = "EntryHi"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 11: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Compare)); +            rn = "Compare"; +            break; +        /* 6,7 are implementation dependent */ +        default: +            goto cp0_unimplemented; +        } +        break; +    case 12: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Status)); +            rn = "Status"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_IntCtl)); +            rn = "IntCtl"; +            break; +        case 2: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSCtl)); +            rn = "SRSCtl"; +            break; +        case 3: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); +            rn = "SRSMap"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 13: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Cause)); +            rn = "Cause"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 14: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); +            rn = "EPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 15: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PRid)); +            rn = "PRid"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_EBase)); +            rn = "EBase"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 16: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config0)); +            rn = "Config"; +            break; +        case 1: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config1)); +            rn = "Config1"; +            break; +        case 2: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config2)); +            rn = "Config2"; +            break; +        case 3: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config3)); +            rn = "Config3"; +            break; +        case 4: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config4)); +            rn = "Config4"; +            break; +        case 5: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config5)); +            rn = "Config5"; +            break; +       /* 6,7 are implementation dependent */ +        case 6: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config6)); +            rn = "Config6"; +            break; +        case 7: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config7)); +            rn = "Config7"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 17: +        switch (sel) { +        case 0: +            gen_helper_dmfc0_lladdr(arg, cpu_env); +            rn = "LLAddr"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 18: +        switch (sel) { +        case 0 ... 7: +            gen_helper_1e0i(dmfc0_watchlo, arg, sel); +            rn = "WatchLo"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 19: +        switch (sel) { +        case 0 ... 7: +            gen_helper_1e0i(mfc0_watchhi, arg, sel); +            rn = "WatchHi"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 20: +        switch (sel) { +        case 0: +            check_insn(ctx, ISA_MIPS3); +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext)); +            rn = "XContext"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 21: +       /* Officially reserved, but sel 0 is used for R1x000 framemask */ +        CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Framemask)); +            rn = "Framemask"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 22: +        tcg_gen_movi_tl(arg, 0); /* unimplemented */ +        rn = "'Diagnostic"; /* implementation dependent */ +        break; +    case 23: +        switch (sel) { +        case 0: +            gen_helper_mfc0_debug(arg, cpu_env); /* EJTAG support */ +            rn = "Debug"; +            break; +        case 1: +//            gen_helper_dmfc0_tracecontrol(arg, cpu_env); /* PDtrace support */ +            rn = "TraceControl"; +//            break; +        case 2: +//            gen_helper_dmfc0_tracecontrol2(arg, cpu_env); /* PDtrace support */ +            rn = "TraceControl2"; +//            break; +        case 3: +//            gen_helper_dmfc0_usertracedata(arg, cpu_env); /* PDtrace support */ +            rn = "UserTraceData"; +//            break; +        case 4: +//            gen_helper_dmfc0_tracebpc(arg, cpu_env); /* PDtrace support */ +            rn = "TraceBPC"; +//            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 24: +        switch (sel) { +        case 0: +            /* EJTAG support */ +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); +            rn = "DEPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 25: +        switch (sel) { +        case 0: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Performance0)); +            rn = "Performance0"; +            break; +        case 1: +//            gen_helper_dmfc0_performance1(arg); +            rn = "Performance1"; +//            break; +        case 2: +//            gen_helper_dmfc0_performance2(arg); +            rn = "Performance2"; +//            break; +        case 3: +//            gen_helper_dmfc0_performance3(arg); +            rn = "Performance3"; +//            break; +        case 4: +//            gen_helper_dmfc0_performance4(arg); +            rn = "Performance4"; +//            break; +        case 5: +//            gen_helper_dmfc0_performance5(arg); +            rn = "Performance5"; +//            break; +        case 6: +//            gen_helper_dmfc0_performance6(arg); +            rn = "Performance6"; +//            break; +        case 7: +//            gen_helper_dmfc0_performance7(arg); +            rn = "Performance7"; +//            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 26: +        tcg_gen_movi_tl(arg, 0); /* unimplemented */ +        rn = "ECC"; +        break; +    case 27: +        switch (sel) { +        /* ignored */ +        case 0 ... 3: +            tcg_gen_movi_tl(arg, 0); /* unimplemented */ +            rn = "CacheErr"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 28: +        switch (sel) { +        case 0: +        case 2: +        case 4: +        case 6: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagLo)); +            rn = "TagLo"; +            break; +        case 1: +        case 3: +        case 5: +        case 7: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataLo)); +            rn = "DataLo"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 29: +        switch (sel) { +        case 0: +        case 2: +        case 4: +        case 6: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagHi)); +            rn = "TagHi"; +            break; +        case 1: +        case 3: +        case 5: +        case 7: +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataHi)); +            rn = "DataHi"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 30: +        switch (sel) { +        case 0: +            tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); +            rn = "ErrorEPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 31: +        switch (sel) { +        case 0: +            /* EJTAG support */ +            gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); +            rn = "DESAVE"; +            break; +        case 2 ... 7: +            CP0_CHECK(ctx->kscrexist & (1 << sel)); +            tcg_gen_ld_tl(arg, cpu_env, +                          offsetof(CPUMIPSState, CP0_KScratch[sel-2])); +            rn = "KScratch"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    default: +        goto cp0_unimplemented; +    } +    (void)rn; /* avoid a compiler warning */ +    LOG_DISAS("dmfc0 %s (reg %d sel %d)\n", rn, reg, sel); +    return; + +cp0_unimplemented: +    LOG_DISAS("dmfc0 %s (reg %d sel %d)\n", rn, reg, sel); +    gen_mfc0_unimplemented(ctx, arg); +} + +static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) +{ +    const char *rn = "invalid"; + +    if (sel != 0) +        check_insn(ctx, ISA_MIPS64); + +    if (ctx->tb->cflags & CF_USE_ICOUNT) { +        gen_io_start(); +    } + +    switch (reg) { +    case 0: +        switch (sel) { +        case 0: +            gen_helper_mtc0_index(cpu_env, arg); +            rn = "Index"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_mvpcontrol(cpu_env, arg); +            rn = "MVPControl"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            /* ignored */ +            rn = "MVPConf0"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            /* ignored */ +            rn = "MVPConf1"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 1: +        switch (sel) { +        case 0: +            /* ignored */ +            rn = "Random"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_vpecontrol(cpu_env, arg); +            rn = "VPEControl"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_vpeconf0(cpu_env, arg); +            rn = "VPEConf0"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_vpeconf1(cpu_env, arg); +            rn = "VPEConf1"; +            break; +        case 4: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_yqmask(cpu_env, arg); +            rn = "YQMask"; +            break; +        case 5: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); +            rn = "VPESchedule"; +            break; +        case 6: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); +            rn = "VPEScheFBack"; +            break; +        case 7: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_vpeopt(cpu_env, arg); +            rn = "VPEOpt"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 2: +        switch (sel) { +        case 0: +            gen_helper_dmtc0_entrylo0(cpu_env, arg); +            rn = "EntryLo0"; +            break; +        case 1: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tcstatus(cpu_env, arg); +            rn = "TCStatus"; +            break; +        case 2: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tcbind(cpu_env, arg); +            rn = "TCBind"; +            break; +        case 3: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tcrestart(cpu_env, arg); +            rn = "TCRestart"; +            break; +        case 4: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tchalt(cpu_env, arg); +            rn = "TCHalt"; +            break; +        case 5: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tccontext(cpu_env, arg); +            rn = "TCContext"; +            break; +        case 6: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tcschedule(cpu_env, arg); +            rn = "TCSchedule"; +            break; +        case 7: +            CP0_CHECK(ctx->insn_flags & ASE_MT); +            gen_helper_mtc0_tcschefback(cpu_env, arg); +            rn = "TCScheFBack"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 3: +        switch (sel) { +        case 0: +            gen_helper_dmtc0_entrylo1(cpu_env, arg); +            rn = "EntryLo1"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 4: +        switch (sel) { +        case 0: +            gen_helper_mtc0_context(cpu_env, arg); +            rn = "Context"; +            break; +        case 1: +//           gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */ +            rn = "ContextConfig"; +            goto cp0_unimplemented; +//           break; +        case 2: +            CP0_CHECK(ctx->ulri); +            tcg_gen_st_tl(arg, cpu_env, +                          offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); +            rn = "UserLocal"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 5: +        switch (sel) { +        case 0: +            gen_helper_mtc0_pagemask(cpu_env, arg); +            rn = "PageMask"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_pagegrain(cpu_env, arg); +            rn = "PageGrain"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 6: +        switch (sel) { +        case 0: +            gen_helper_mtc0_wired(cpu_env, arg); +            rn = "Wired"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsconf0(cpu_env, arg); +            rn = "SRSConf0"; +            break; +        case 2: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsconf1(cpu_env, arg); +            rn = "SRSConf1"; +            break; +        case 3: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsconf2(cpu_env, arg); +            rn = "SRSConf2"; +            break; +        case 4: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsconf3(cpu_env, arg); +            rn = "SRSConf3"; +            break; +        case 5: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsconf4(cpu_env, arg); +            rn = "SRSConf4"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 7: +        switch (sel) { +        case 0: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_hwrena(cpu_env, arg); +            ctx->bstate = BS_STOP; +            rn = "HWREna"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 8: +        switch (sel) { +        case 0: +            /* ignored */ +            rn = "BadVAddr"; +            break; +        case 1: +            /* ignored */ +            rn = "BadInstr"; +            break; +        case 2: +            /* ignored */ +            rn = "BadInstrP"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 9: +        switch (sel) { +        case 0: +            gen_helper_mtc0_count(cpu_env, arg); +            rn = "Count"; +            break; +        /* 6,7 are implementation dependent */ +        default: +            goto cp0_unimplemented; +        } +        /* Stop translation as we may have switched the execution mode */ +        ctx->bstate = BS_STOP; +        break; +    case 10: +        switch (sel) { +        case 0: +            gen_helper_mtc0_entryhi(cpu_env, arg); +            rn = "EntryHi"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 11: +        switch (sel) { +        case 0: +            gen_helper_mtc0_compare(cpu_env, arg); +            rn = "Compare"; +            break; +        /* 6,7 are implementation dependent */ +        default: +            goto cp0_unimplemented; +        } +        /* Stop translation as we may have switched the execution mode */ +        ctx->bstate = BS_STOP; +        break; +    case 12: +        switch (sel) { +        case 0: +            save_cpu_state(ctx, 1); +            gen_helper_mtc0_status(cpu_env, arg); +            /* BS_STOP isn't good enough here, hflags may have changed. */ +            gen_save_pc(ctx->pc + 4); +            ctx->bstate = BS_EXCP; +            rn = "Status"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_intctl(cpu_env, arg); +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "IntCtl"; +            break; +        case 2: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_srsctl(cpu_env, arg); +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "SRSCtl"; +            break; +        case 3: +            check_insn(ctx, ISA_MIPS32R2); +            gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "SRSMap"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 13: +        switch (sel) { +        case 0: +            save_cpu_state(ctx, 1); +            /* Mark as an IO operation because we may trigger a software +               interrupt.  */ +            if (ctx->tb->cflags & CF_USE_ICOUNT) { +                gen_io_start(); +            } +            gen_helper_mtc0_cause(cpu_env, arg); +            if (ctx->tb->cflags & CF_USE_ICOUNT) { +                gen_io_end(); +            } +            /* Stop translation as we may have triggered an intetrupt */ +            ctx->bstate = BS_STOP; +            rn = "Cause"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 14: +        switch (sel) { +        case 0: +            tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); +            rn = "EPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 15: +        switch (sel) { +        case 0: +            /* ignored */ +            rn = "PRid"; +            break; +        case 1: +            check_insn(ctx, ISA_MIPS32R2); +            gen_helper_mtc0_ebase(cpu_env, arg); +            rn = "EBase"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 16: +        switch (sel) { +        case 0: +            gen_helper_mtc0_config0(cpu_env, arg); +            rn = "Config"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            break; +        case 1: +            /* ignored, read only */ +            rn = "Config1"; +            break; +        case 2: +            gen_helper_mtc0_config2(cpu_env, arg); +            rn = "Config2"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            break; +        case 3: +            gen_helper_mtc0_config3(cpu_env, arg); +            rn = "Config3"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            break; +        case 4: +            /* currently ignored */ +            rn = "Config4"; +            break; +        case 5: +            gen_helper_mtc0_config5(cpu_env, arg); +            rn = "Config5"; +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            break; +        /* 6,7 are implementation dependent */ +        default: +            rn = "Invalid config selector"; +            goto cp0_unimplemented; +        } +        break; +    case 17: +        switch (sel) { +        case 0: +            gen_helper_mtc0_lladdr(cpu_env, arg); +            rn = "LLAddr"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 18: +        switch (sel) { +        case 0 ... 7: +            gen_helper_0e1i(mtc0_watchlo, arg, sel); +            rn = "WatchLo"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 19: +        switch (sel) { +        case 0 ... 7: +            gen_helper_0e1i(mtc0_watchhi, arg, sel); +            rn = "WatchHi"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 20: +        switch (sel) { +        case 0: +            check_insn(ctx, ISA_MIPS3); +            gen_helper_mtc0_xcontext(cpu_env, arg); +            rn = "XContext"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 21: +       /* Officially reserved, but sel 0 is used for R1x000 framemask */ +        CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); +        switch (sel) { +        case 0: +            gen_helper_mtc0_framemask(cpu_env, arg); +            rn = "Framemask"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 22: +        /* ignored */ +        rn = "Diagnostic"; /* implementation dependent */ +        break; +    case 23: +        switch (sel) { +        case 0: +            gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ +            /* BS_STOP isn't good enough here, hflags may have changed. */ +            gen_save_pc(ctx->pc + 4); +            ctx->bstate = BS_EXCP; +            rn = "Debug"; +            break; +        case 1: +//            gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */ +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "TraceControl"; +//            break; +        case 2: +//            gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */ +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "TraceControl2"; +//            break; +        case 3: +//            gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */ +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "UserTraceData"; +//            break; +        case 4: +//            gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */ +            /* Stop translation as we may have switched the execution mode */ +            ctx->bstate = BS_STOP; +            rn = "TraceBPC"; +//            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 24: +        switch (sel) { +        case 0: +            /* EJTAG support */ +            tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); +            rn = "DEPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 25: +        switch (sel) { +        case 0: +            gen_helper_mtc0_performance0(cpu_env, arg); +            rn = "Performance0"; +            break; +        case 1: +//            gen_helper_mtc0_performance1(cpu_env, arg); +            rn = "Performance1"; +//            break; +        case 2: +//            gen_helper_mtc0_performance2(cpu_env, arg); +            rn = "Performance2"; +//            break; +        case 3: +//            gen_helper_mtc0_performance3(cpu_env, arg); +            rn = "Performance3"; +//            break; +        case 4: +//            gen_helper_mtc0_performance4(cpu_env, arg); +            rn = "Performance4"; +//            break; +        case 5: +//            gen_helper_mtc0_performance5(cpu_env, arg); +            rn = "Performance5"; +//            break; +        case 6: +//            gen_helper_mtc0_performance6(cpu_env, arg); +            rn = "Performance6"; +//            break; +        case 7: +//            gen_helper_mtc0_performance7(cpu_env, arg); +            rn = "Performance7"; +//            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 26: +        /* ignored */ +        rn = "ECC"; +        break; +    case 27: +        switch (sel) { +        case 0 ... 3: +            /* ignored */ +            rn = "CacheErr"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 28: +        switch (sel) { +        case 0: +        case 2: +        case 4: +        case 6: +            gen_helper_mtc0_taglo(cpu_env, arg); +            rn = "TagLo"; +            break; +        case 1: +        case 3: +        case 5: +        case 7: +            gen_helper_mtc0_datalo(cpu_env, arg); +            rn = "DataLo"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 29: +        switch (sel) { +        case 0: +        case 2: +        case 4: +        case 6: +            gen_helper_mtc0_taghi(cpu_env, arg); +            rn = "TagHi"; +            break; +        case 1: +        case 3: +        case 5: +        case 7: +            gen_helper_mtc0_datahi(cpu_env, arg); +            rn = "DataHi"; +            break; +        default: +            rn = "invalid sel"; +            goto cp0_unimplemented; +        } +        break; +    case 30: +        switch (sel) { +        case 0: +            tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); +            rn = "ErrorEPC"; +            break; +        default: +            goto cp0_unimplemented; +        } +        break; +    case 31: +        switch (sel) { +        case 0: +            /* EJTAG support */ +            gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); +            rn = "DESAVE"; +            break; +        case 2 ... 7: +            CP0_CHECK(ctx->kscrexist & (1 << sel)); +            tcg_gen_st_tl(arg, cpu_env, +                          offsetof(CPUMIPSState, CP0_KScratch[sel-2])); +            rn = "KScratch"; +            break; +        default: +            goto cp0_unimplemented; +        } +        /* Stop translation as we may have switched the execution mode */ +        ctx->bstate = BS_STOP; +        break; +    default: +        goto cp0_unimplemented; +    } +    (void)rn; /* avoid a compiler warning */ +    LOG_DISAS("dmtc0 %s (reg %d sel %d)\n", rn, reg, sel); +    /* For simplicity assume that all writes can cause interrupts.  */ +    if (ctx->tb->cflags & CF_USE_ICOUNT) { +        gen_io_end(); +        ctx->bstate = BS_STOP; +    } +    return; + +cp0_unimplemented: +    LOG_DISAS("dmtc0 %s (reg %d sel %d)\n", rn, reg, sel); +} +#endif /* TARGET_MIPS64 */ + +static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, +                     int u, int sel, int h) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    TCGv t0 = tcg_temp_local_new(); + +    if ((env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) == 0 && +        ((env->tcs[other_tc].CP0_TCBind & (0xf << CP0TCBd_CurVPE)) != +         (env->active_tc.CP0_TCBind & (0xf << CP0TCBd_CurVPE)))) +        tcg_gen_movi_tl(t0, -1); +    else if ((env->CP0_VPEControl & (0xff << CP0VPECo_TargTC)) > +             (env->mvp->CP0_MVPConf0 & (0xff << CP0MVPC0_PTC))) +        tcg_gen_movi_tl(t0, -1); +    else if (u == 0) { +        switch (rt) { +        case 1: +            switch (sel) { +            case 1: +                gen_helper_mftc0_vpecontrol(t0, cpu_env); +                break; +            case 2: +                gen_helper_mftc0_vpeconf0(t0, cpu_env); +                break; +            default: +                goto die; +                break; +            } +            break; +        case 2: +            switch (sel) { +            case 1: +                gen_helper_mftc0_tcstatus(t0, cpu_env); +                break; +            case 2: +                gen_helper_mftc0_tcbind(t0, cpu_env); +                break; +            case 3: +                gen_helper_mftc0_tcrestart(t0, cpu_env); +                break; +            case 4: +                gen_helper_mftc0_tchalt(t0, cpu_env); +                break; +            case 5: +                gen_helper_mftc0_tccontext(t0, cpu_env); +                break; +            case 6: +                gen_helper_mftc0_tcschedule(t0, cpu_env); +                break; +            case 7: +                gen_helper_mftc0_tcschefback(t0, cpu_env); +                break; +            default: +                gen_mfc0(ctx, t0, rt, sel); +                break; +            } +            break; +        case 10: +            switch (sel) { +            case 0: +                gen_helper_mftc0_entryhi(t0, cpu_env); +                break; +            default: +                gen_mfc0(ctx, t0, rt, sel); +                break; +            } +        case 12: +            switch (sel) { +            case 0: +                gen_helper_mftc0_status(t0, cpu_env); +                break; +            default: +                gen_mfc0(ctx, t0, rt, sel); +                break; +            } +        case 13: +            switch (sel) { +            case 0: +                gen_helper_mftc0_cause(t0, cpu_env); +                break; +            default: +                goto die; +                break; +            } +            break; +        case 14: +            switch (sel) { +            case 0: +                gen_helper_mftc0_epc(t0, cpu_env); +                break; +            default: +                goto die; +                break; +            } +            break; +        case 15: +            switch (sel) { +            case 1: +                gen_helper_mftc0_ebase(t0, cpu_env); +                break; +            default: +                goto die; +                break; +            } +            break; +        case 16: +            switch (sel) { +            case 0 ... 7: +                gen_helper_mftc0_configx(t0, cpu_env, tcg_const_tl(sel)); +                break; +            default: +                goto die; +                break; +            } +            break; +        case 23: +            switch (sel) { +            case 0: +                gen_helper_mftc0_debug(t0, cpu_env); +                break; +            default: +                gen_mfc0(ctx, t0, rt, sel); +                break; +            } +            break; +        default: +            gen_mfc0(ctx, t0, rt, sel); +        } +    } else switch (sel) { +    /* GPR registers. */ +    case 0: +        gen_helper_1e0i(mftgpr, t0, rt); +        break; +    /* Auxiliary CPU registers */ +    case 1: +        switch (rt) { +        case 0: +            gen_helper_1e0i(mftlo, t0, 0); +            break; +        case 1: +            gen_helper_1e0i(mfthi, t0, 0); +            break; +        case 2: +            gen_helper_1e0i(mftacx, t0, 0); +            break; +        case 4: +            gen_helper_1e0i(mftlo, t0, 1); +            break; +        case 5: +            gen_helper_1e0i(mfthi, t0, 1); +            break; +        case 6: +            gen_helper_1e0i(mftacx, t0, 1); +            break; +        case 8: +            gen_helper_1e0i(mftlo, t0, 2); +            break; +        case 9: +            gen_helper_1e0i(mfthi, t0, 2); +            break; +        case 10: +            gen_helper_1e0i(mftacx, t0, 2); +            break; +        case 12: +            gen_helper_1e0i(mftlo, t0, 3); +            break; +        case 13: +            gen_helper_1e0i(mfthi, t0, 3); +            break; +        case 14: +            gen_helper_1e0i(mftacx, t0, 3); +            break; +        case 16: +            gen_helper_mftdsp(t0, cpu_env); +            break; +        default: +            goto die; +        } +        break; +    /* Floating point (COP1). */ +    case 2: +        /* XXX: For now we support only a single FPU context. */ +        if (h == 0) { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, rt); +            tcg_gen_ext_i32_tl(t0, fp0); +            tcg_temp_free_i32(fp0); +        } else { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32h(ctx, fp0, rt); +            tcg_gen_ext_i32_tl(t0, fp0); +            tcg_temp_free_i32(fp0); +        } +        break; +    case 3: +        /* XXX: For now we support only a single FPU context. */ +        gen_helper_1e0i(cfc1, t0, rt); +        break; +    /* COP2: Not implemented. */ +    case 4: +    case 5: +        /* fall through */ +    default: +        goto die; +    } +    LOG_DISAS("mftr (reg %d u %d sel %d h %d)\n", rt, u, sel, h); +    gen_store_gpr(t0, rd); +    tcg_temp_free(t0); +    return; + +die: +    tcg_temp_free(t0); +    LOG_DISAS("mftr (reg %d u %d sel %d h %d)\n", rt, u, sel, h); +    generate_exception(ctx, EXCP_RI); +} + +static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, +                     int u, int sel, int h) +{ +    int other_tc = env->CP0_VPEControl & (0xff << CP0VPECo_TargTC); +    TCGv t0 = tcg_temp_local_new(); + +    gen_load_gpr(t0, rt); +    if ((env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) == 0 && +        ((env->tcs[other_tc].CP0_TCBind & (0xf << CP0TCBd_CurVPE)) != +         (env->active_tc.CP0_TCBind & (0xf << CP0TCBd_CurVPE)))) +        /* NOP */ ; +    else if ((env->CP0_VPEControl & (0xff << CP0VPECo_TargTC)) > +             (env->mvp->CP0_MVPConf0 & (0xff << CP0MVPC0_PTC))) +        /* NOP */ ; +    else if (u == 0) { +        switch (rd) { +        case 1: +            switch (sel) { +            case 1: +                gen_helper_mttc0_vpecontrol(cpu_env, t0); +                break; +            case 2: +                gen_helper_mttc0_vpeconf0(cpu_env, t0); +                break; +            default: +                goto die; +                break; +            } +            break; +        case 2: +            switch (sel) { +            case 1: +                gen_helper_mttc0_tcstatus(cpu_env, t0); +                break; +            case 2: +                gen_helper_mttc0_tcbind(cpu_env, t0); +                break; +            case 3: +                gen_helper_mttc0_tcrestart(cpu_env, t0); +                break; +            case 4: +                gen_helper_mttc0_tchalt(cpu_env, t0); +                break; +            case 5: +                gen_helper_mttc0_tccontext(cpu_env, t0); +                break; +            case 6: +                gen_helper_mttc0_tcschedule(cpu_env, t0); +                break; +            case 7: +                gen_helper_mttc0_tcschefback(cpu_env, t0); +                break; +            default: +                gen_mtc0(ctx, t0, rd, sel); +                break; +            } +            break; +        case 10: +            switch (sel) { +            case 0: +                gen_helper_mttc0_entryhi(cpu_env, t0); +                break; +            default: +                gen_mtc0(ctx, t0, rd, sel); +                break; +            } +        case 12: +            switch (sel) { +            case 0: +                gen_helper_mttc0_status(cpu_env, t0); +                break; +            default: +                gen_mtc0(ctx, t0, rd, sel); +                break; +            } +        case 13: +            switch (sel) { +            case 0: +                gen_helper_mttc0_cause(cpu_env, t0); +                break; +            default: +                goto die; +                break; +            } +            break; +        case 15: +            switch (sel) { +            case 1: +                gen_helper_mttc0_ebase(cpu_env, t0); +                break; +            default: +                goto die; +                break; +            } +            break; +        case 23: +            switch (sel) { +            case 0: +                gen_helper_mttc0_debug(cpu_env, t0); +                break; +            default: +                gen_mtc0(ctx, t0, rd, sel); +                break; +            } +            break; +        default: +            gen_mtc0(ctx, t0, rd, sel); +        } +    } else switch (sel) { +    /* GPR registers. */ +    case 0: +        gen_helper_0e1i(mttgpr, t0, rd); +        break; +    /* Auxiliary CPU registers */ +    case 1: +        switch (rd) { +        case 0: +            gen_helper_0e1i(mttlo, t0, 0); +            break; +        case 1: +            gen_helper_0e1i(mtthi, t0, 0); +            break; +        case 2: +            gen_helper_0e1i(mttacx, t0, 0); +            break; +        case 4: +            gen_helper_0e1i(mttlo, t0, 1); +            break; +        case 5: +            gen_helper_0e1i(mtthi, t0, 1); +            break; +        case 6: +            gen_helper_0e1i(mttacx, t0, 1); +            break; +        case 8: +            gen_helper_0e1i(mttlo, t0, 2); +            break; +        case 9: +            gen_helper_0e1i(mtthi, t0, 2); +            break; +        case 10: +            gen_helper_0e1i(mttacx, t0, 2); +            break; +        case 12: +            gen_helper_0e1i(mttlo, t0, 3); +            break; +        case 13: +            gen_helper_0e1i(mtthi, t0, 3); +            break; +        case 14: +            gen_helper_0e1i(mttacx, t0, 3); +            break; +        case 16: +            gen_helper_mttdsp(cpu_env, t0); +            break; +        default: +            goto die; +        } +        break; +    /* Floating point (COP1). */ +    case 2: +        /* XXX: For now we support only a single FPU context. */ +        if (h == 0) { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            tcg_gen_trunc_tl_i32(fp0, t0); +            gen_store_fpr32(ctx, fp0, rd); +            tcg_temp_free_i32(fp0); +        } else { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            tcg_gen_trunc_tl_i32(fp0, t0); +            gen_store_fpr32h(ctx, fp0, rd); +            tcg_temp_free_i32(fp0); +        } +        break; +    case 3: +        /* XXX: For now we support only a single FPU context. */ +        save_cpu_state(ctx, 1); +        { +            TCGv_i32 fs_tmp = tcg_const_i32(rd); + +            gen_helper_0e2i(ctc1, t0, fs_tmp, rt); +            tcg_temp_free_i32(fs_tmp); +        } +        /* Stop translation as we may have changed hflags */ +        ctx->bstate = BS_STOP; +        break; +    /* COP2: Not implemented. */ +    case 4: +    case 5: +        /* fall through */ +    default: +        goto die; +    } +    LOG_DISAS("mttr (reg %d u %d sel %d h %d)\n", rd, u, sel, h); +    tcg_temp_free(t0); +    return; + +die: +    tcg_temp_free(t0); +    LOG_DISAS("mttr (reg %d u %d sel %d h %d)\n", rd, u, sel, h); +    generate_exception(ctx, EXCP_RI); +} + +static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, int rd) +{ +    const char *opn = "ldst"; + +    check_cp0_enabled(ctx); +    switch (opc) { +    case OPC_MFC0: +        if (rt == 0) { +            /* Treat as NOP. */ +            return; +        } +        gen_mfc0(ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); +        opn = "mfc0"; +        break; +    case OPC_MTC0: +        { +            TCGv t0 = tcg_temp_new(); + +            gen_load_gpr(t0, rt); +            gen_mtc0(ctx, t0, rd, ctx->opcode & 0x7); +            tcg_temp_free(t0); +        } +        opn = "mtc0"; +        break; +#if defined(TARGET_MIPS64) +    case OPC_DMFC0: +        check_insn(ctx, ISA_MIPS3); +        if (rt == 0) { +            /* Treat as NOP. */ +            return; +        } +        gen_dmfc0(ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); +        opn = "dmfc0"; +        break; +    case OPC_DMTC0: +        check_insn(ctx, ISA_MIPS3); +        { +            TCGv t0 = tcg_temp_new(); + +            gen_load_gpr(t0, rt); +            gen_dmtc0(ctx, t0, rd, ctx->opcode & 0x7); +            tcg_temp_free(t0); +        } +        opn = "dmtc0"; +        break; +#endif +    case OPC_MFHC0: +        check_mvh(ctx); +        if (rt == 0) { +            /* Treat as NOP. */ +            return; +        } +        gen_mfhc0(ctx, cpu_gpr[rt], rd, ctx->opcode & 0x7); +        opn = "mfhc0"; +        break; +    case OPC_MTHC0: +        check_mvh(ctx); +        { +            TCGv t0 = tcg_temp_new(); +            gen_load_gpr(t0, rt); +            gen_mthc0(ctx, t0, rd, ctx->opcode & 0x7); +            tcg_temp_free(t0); +        } +        opn = "mthc0"; +        break; +    case OPC_MFTR: +        check_insn(ctx, ASE_MT); +        if (rd == 0) { +            /* Treat as NOP. */ +            return; +        } +        gen_mftr(env, ctx, rt, rd, (ctx->opcode >> 5) & 1, +                 ctx->opcode & 0x7, (ctx->opcode >> 4) & 1); +        opn = "mftr"; +        break; +    case OPC_MTTR: +        check_insn(ctx, ASE_MT); +        gen_mttr(env, ctx, rd, rt, (ctx->opcode >> 5) & 1, +                 ctx->opcode & 0x7, (ctx->opcode >> 4) & 1); +        opn = "mttr"; +        break; +    case OPC_TLBWI: +        opn = "tlbwi"; +        if (!env->tlb->helper_tlbwi) +            goto die; +        gen_helper_tlbwi(cpu_env); +        break; +    case OPC_TLBINV: +        opn = "tlbinv"; +        if (ctx->ie >= 2) { +            if (!env->tlb->helper_tlbinv) { +                goto die; +            } +            gen_helper_tlbinv(cpu_env); +        } /* treat as nop if TLBINV not supported */ +        break; +    case OPC_TLBINVF: +        opn = "tlbinvf"; +        if (ctx->ie >= 2) { +            if (!env->tlb->helper_tlbinvf) { +                goto die; +            } +            gen_helper_tlbinvf(cpu_env); +        } /* treat as nop if TLBINV not supported */ +        break; +    case OPC_TLBWR: +        opn = "tlbwr"; +        if (!env->tlb->helper_tlbwr) +            goto die; +        gen_helper_tlbwr(cpu_env); +        break; +    case OPC_TLBP: +        opn = "tlbp"; +        if (!env->tlb->helper_tlbp) +            goto die; +        gen_helper_tlbp(cpu_env); +        break; +    case OPC_TLBR: +        opn = "tlbr"; +        if (!env->tlb->helper_tlbr) +            goto die; +        gen_helper_tlbr(cpu_env); +        break; +    case OPC_ERET: /* OPC_ERETNC */ +        if ((ctx->insn_flags & ISA_MIPS32R6) && +            (ctx->hflags & MIPS_HFLAG_BMASK)) { +            MIPS_DEBUG("CTI in delay / forbidden slot"); +            goto die; +        } else { +            int bit_shift = (ctx->hflags & MIPS_HFLAG_M16) ? 16 : 6; +            if (ctx->opcode & (1 << bit_shift)) { +                /* OPC_ERETNC */ +                opn = "eretnc"; +                check_insn(ctx, ISA_MIPS32R5); +                gen_helper_eretnc(cpu_env); +            } else { +                /* OPC_ERET */ +                opn = "eret"; +                check_insn(ctx, ISA_MIPS2); +                gen_helper_eret(cpu_env); +            } +            ctx->bstate = BS_EXCP; +        } +        break; +    case OPC_DERET: +        opn = "deret"; +        check_insn(ctx, ISA_MIPS32); +        if ((ctx->insn_flags & ISA_MIPS32R6) && +            (ctx->hflags & MIPS_HFLAG_BMASK)) { +            MIPS_DEBUG("CTI in delay / forbidden slot"); +            goto die; +        } +        if (!(ctx->hflags & MIPS_HFLAG_DM)) { +            MIPS_INVAL(opn); +            generate_exception(ctx, EXCP_RI); +        } else { +            gen_helper_deret(cpu_env); +            ctx->bstate = BS_EXCP; +        } +        break; +    case OPC_WAIT: +        opn = "wait"; +        check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); +        if ((ctx->insn_flags & ISA_MIPS32R6) && +            (ctx->hflags & MIPS_HFLAG_BMASK)) { +            MIPS_DEBUG("CTI in delay / forbidden slot"); +            goto die; +        } +        /* If we get an exception, we want to restart at next instruction */ +        ctx->pc += 4; +        save_cpu_state(ctx, 1); +        ctx->pc -= 4; +        gen_helper_wait(cpu_env); +        ctx->bstate = BS_EXCP; +        break; +    default: + die: +        MIPS_INVAL(opn); +        generate_exception(ctx, EXCP_RI); +        return; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s %d", opn, regnames[rt], rd); +} +#endif /* !CONFIG_USER_ONLY */ + +/* CP1 Branches (before delay slot) */ +static void gen_compute_branch1(DisasContext *ctx, uint32_t op, +                                int32_t cc, int32_t offset) +{ +    target_ulong btarget; +    const char *opn = "cp1 cond branch"; +    TCGv_i32 t0 = tcg_temp_new_i32(); + +    if ((ctx->insn_flags & ISA_MIPS32R6) && (ctx->hflags & MIPS_HFLAG_BMASK)) { +        MIPS_DEBUG("CTI in delay / forbidden slot"); +        generate_exception(ctx, EXCP_RI); +        goto out; +    } + +    if (cc != 0) +        check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); + +    btarget = ctx->pc + 4 + offset; + +    switch (op) { +    case OPC_BC1F: +        tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); +        tcg_gen_not_i32(t0, t0); +        tcg_gen_andi_i32(t0, t0, 1); +        tcg_gen_extu_i32_tl(bcond, t0); +        opn = "bc1f"; +        goto not_likely; +    case OPC_BC1FL: +        tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); +        tcg_gen_not_i32(t0, t0); +        tcg_gen_andi_i32(t0, t0, 1); +        tcg_gen_extu_i32_tl(bcond, t0); +        opn = "bc1fl"; +        goto likely; +    case OPC_BC1T: +        tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); +        tcg_gen_andi_i32(t0, t0, 1); +        tcg_gen_extu_i32_tl(bcond, t0); +        opn = "bc1t"; +        goto not_likely; +    case OPC_BC1TL: +        tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); +        tcg_gen_andi_i32(t0, t0, 1); +        tcg_gen_extu_i32_tl(bcond, t0); +        opn = "bc1tl"; +    likely: +        ctx->hflags |= MIPS_HFLAG_BL; +        break; +    case OPC_BC1FANY2: +        { +            TCGv_i32 t1 = tcg_temp_new_i32(); +            tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); +            tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+1)); +            tcg_gen_nand_i32(t0, t0, t1); +            tcg_temp_free_i32(t1); +            tcg_gen_andi_i32(t0, t0, 1); +            tcg_gen_extu_i32_tl(bcond, t0); +        } +        opn = "bc1any2f"; +        goto not_likely; +    case OPC_BC1TANY2: +        { +            TCGv_i32 t1 = tcg_temp_new_i32(); +            tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); +            tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+1)); +            tcg_gen_or_i32(t0, t0, t1); +            tcg_temp_free_i32(t1); +            tcg_gen_andi_i32(t0, t0, 1); +            tcg_gen_extu_i32_tl(bcond, t0); +        } +        opn = "bc1any2t"; +        goto not_likely; +    case OPC_BC1FANY4: +        { +            TCGv_i32 t1 = tcg_temp_new_i32(); +            tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); +            tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+1)); +            tcg_gen_and_i32(t0, t0, t1); +            tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+2)); +            tcg_gen_and_i32(t0, t0, t1); +            tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+3)); +            tcg_gen_nand_i32(t0, t0, t1); +            tcg_temp_free_i32(t1); +            tcg_gen_andi_i32(t0, t0, 1); +            tcg_gen_extu_i32_tl(bcond, t0); +        } +        opn = "bc1any4f"; +        goto not_likely; +    case OPC_BC1TANY4: +        { +            TCGv_i32 t1 = tcg_temp_new_i32(); +            tcg_gen_shri_i32(t0, fpu_fcr31, get_fp_bit(cc)); +            tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+1)); +            tcg_gen_or_i32(t0, t0, t1); +            tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+2)); +            tcg_gen_or_i32(t0, t0, t1); +            tcg_gen_shri_i32(t1, fpu_fcr31, get_fp_bit(cc+3)); +            tcg_gen_or_i32(t0, t0, t1); +            tcg_temp_free_i32(t1); +            tcg_gen_andi_i32(t0, t0, 1); +            tcg_gen_extu_i32_tl(bcond, t0); +        } +        opn = "bc1any4t"; +    not_likely: +        ctx->hflags |= MIPS_HFLAG_BC; +        break; +    default: +        MIPS_INVAL(opn); +        generate_exception (ctx, EXCP_RI); +        goto out; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn, +               ctx->hflags, btarget); +    ctx->btarget = btarget; +    ctx->hflags |= MIPS_HFLAG_BDS32; + out: +    tcg_temp_free_i32(t0); +} + +/* R6 CP1 Branches */ +static void gen_compute_branch1_r6(DisasContext *ctx, uint32_t op, +                                   int32_t ft, int32_t offset, +                                   int delayslot_size) +{ +    target_ulong btarget; +    const char *opn = "cp1 cond branch"; +    TCGv_i64 t0 = tcg_temp_new_i64(); + +    if (ctx->hflags & MIPS_HFLAG_BMASK) { +#ifdef MIPS_DEBUG_DISAS +        LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx +                  "\n", ctx->pc); +#endif +        generate_exception(ctx, EXCP_RI); +        goto out; +    } + +    gen_load_fpr64(ctx, t0, ft); +    tcg_gen_andi_i64(t0, t0, 1); + +    btarget = addr_add(ctx, ctx->pc + 4, offset); + +    switch (op) { +    case OPC_BC1EQZ: +        tcg_gen_xori_i64(t0, t0, 1); +        opn = "bc1eqz"; +        ctx->hflags |= MIPS_HFLAG_BC; +        break; +    case OPC_BC1NEZ: +        /* t0 already set */ +        opn = "bc1nez"; +        ctx->hflags |= MIPS_HFLAG_BC; +        break; +    default: +        MIPS_INVAL(opn); +        generate_exception(ctx, EXCP_RI); +        goto out; +    } + +    tcg_gen_trunc_i64_tl(bcond, t0); + +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s: cond %02x target " TARGET_FMT_lx, opn, +               ctx->hflags, btarget); +    ctx->btarget = btarget; + +    switch (delayslot_size) { +    case 2: +        ctx->hflags |= MIPS_HFLAG_BDS16; +        break; +    case 4: +        ctx->hflags |= MIPS_HFLAG_BDS32; +        break; +    } + +out: +    tcg_temp_free_i64(t0); +} + +/* Coprocessor 1 (FPU) */ + +#define FOP(func, fmt) (((fmt) << 21) | (func)) + +enum fopcode { +    OPC_ADD_S = FOP(0, FMT_S), +    OPC_SUB_S = FOP(1, FMT_S), +    OPC_MUL_S = FOP(2, FMT_S), +    OPC_DIV_S = FOP(3, FMT_S), +    OPC_SQRT_S = FOP(4, FMT_S), +    OPC_ABS_S = FOP(5, FMT_S), +    OPC_MOV_S = FOP(6, FMT_S), +    OPC_NEG_S = FOP(7, FMT_S), +    OPC_ROUND_L_S = FOP(8, FMT_S), +    OPC_TRUNC_L_S = FOP(9, FMT_S), +    OPC_CEIL_L_S = FOP(10, FMT_S), +    OPC_FLOOR_L_S = FOP(11, FMT_S), +    OPC_ROUND_W_S = FOP(12, FMT_S), +    OPC_TRUNC_W_S = FOP(13, FMT_S), +    OPC_CEIL_W_S = FOP(14, FMT_S), +    OPC_FLOOR_W_S = FOP(15, FMT_S), +    OPC_SEL_S = FOP(16, FMT_S), +    OPC_MOVCF_S = FOP(17, FMT_S), +    OPC_MOVZ_S = FOP(18, FMT_S), +    OPC_MOVN_S = FOP(19, FMT_S), +    OPC_SELEQZ_S = FOP(20, FMT_S), +    OPC_RECIP_S = FOP(21, FMT_S), +    OPC_RSQRT_S = FOP(22, FMT_S), +    OPC_SELNEZ_S = FOP(23, FMT_S), +    OPC_MADDF_S = FOP(24, FMT_S), +    OPC_MSUBF_S = FOP(25, FMT_S), +    OPC_RINT_S = FOP(26, FMT_S), +    OPC_CLASS_S = FOP(27, FMT_S), +    OPC_MIN_S = FOP(28, FMT_S), +    OPC_RECIP2_S = FOP(28, FMT_S), +    OPC_MINA_S = FOP(29, FMT_S), +    OPC_RECIP1_S = FOP(29, FMT_S), +    OPC_MAX_S = FOP(30, FMT_S), +    OPC_RSQRT1_S = FOP(30, FMT_S), +    OPC_MAXA_S = FOP(31, FMT_S), +    OPC_RSQRT2_S = FOP(31, FMT_S), +    OPC_CVT_D_S = FOP(33, FMT_S), +    OPC_CVT_W_S = FOP(36, FMT_S), +    OPC_CVT_L_S = FOP(37, FMT_S), +    OPC_CVT_PS_S = FOP(38, FMT_S), +    OPC_CMP_F_S = FOP (48, FMT_S), +    OPC_CMP_UN_S = FOP (49, FMT_S), +    OPC_CMP_EQ_S = FOP (50, FMT_S), +    OPC_CMP_UEQ_S = FOP (51, FMT_S), +    OPC_CMP_OLT_S = FOP (52, FMT_S), +    OPC_CMP_ULT_S = FOP (53, FMT_S), +    OPC_CMP_OLE_S = FOP (54, FMT_S), +    OPC_CMP_ULE_S = FOP (55, FMT_S), +    OPC_CMP_SF_S = FOP (56, FMT_S), +    OPC_CMP_NGLE_S = FOP (57, FMT_S), +    OPC_CMP_SEQ_S = FOP (58, FMT_S), +    OPC_CMP_NGL_S = FOP (59, FMT_S), +    OPC_CMP_LT_S = FOP (60, FMT_S), +    OPC_CMP_NGE_S = FOP (61, FMT_S), +    OPC_CMP_LE_S = FOP (62, FMT_S), +    OPC_CMP_NGT_S = FOP (63, FMT_S), + +    OPC_ADD_D = FOP(0, FMT_D), +    OPC_SUB_D = FOP(1, FMT_D), +    OPC_MUL_D = FOP(2, FMT_D), +    OPC_DIV_D = FOP(3, FMT_D), +    OPC_SQRT_D = FOP(4, FMT_D), +    OPC_ABS_D = FOP(5, FMT_D), +    OPC_MOV_D = FOP(6, FMT_D), +    OPC_NEG_D = FOP(7, FMT_D), +    OPC_ROUND_L_D = FOP(8, FMT_D), +    OPC_TRUNC_L_D = FOP(9, FMT_D), +    OPC_CEIL_L_D = FOP(10, FMT_D), +    OPC_FLOOR_L_D = FOP(11, FMT_D), +    OPC_ROUND_W_D = FOP(12, FMT_D), +    OPC_TRUNC_W_D = FOP(13, FMT_D), +    OPC_CEIL_W_D = FOP(14, FMT_D), +    OPC_FLOOR_W_D = FOP(15, FMT_D), +    OPC_SEL_D = FOP(16, FMT_D), +    OPC_MOVCF_D = FOP(17, FMT_D), +    OPC_MOVZ_D = FOP(18, FMT_D), +    OPC_MOVN_D = FOP(19, FMT_D), +    OPC_SELEQZ_D = FOP(20, FMT_D), +    OPC_RECIP_D = FOP(21, FMT_D), +    OPC_RSQRT_D = FOP(22, FMT_D), +    OPC_SELNEZ_D = FOP(23, FMT_D), +    OPC_MADDF_D = FOP(24, FMT_D), +    OPC_MSUBF_D = FOP(25, FMT_D), +    OPC_RINT_D = FOP(26, FMT_D), +    OPC_CLASS_D = FOP(27, FMT_D), +    OPC_MIN_D = FOP(28, FMT_D), +    OPC_RECIP2_D = FOP(28, FMT_D), +    OPC_MINA_D = FOP(29, FMT_D), +    OPC_RECIP1_D = FOP(29, FMT_D), +    OPC_MAX_D = FOP(30, FMT_D), +    OPC_RSQRT1_D = FOP(30, FMT_D), +    OPC_MAXA_D = FOP(31, FMT_D), +    OPC_RSQRT2_D = FOP(31, FMT_D), +    OPC_CVT_S_D = FOP(32, FMT_D), +    OPC_CVT_W_D = FOP(36, FMT_D), +    OPC_CVT_L_D = FOP(37, FMT_D), +    OPC_CMP_F_D = FOP (48, FMT_D), +    OPC_CMP_UN_D = FOP (49, FMT_D), +    OPC_CMP_EQ_D = FOP (50, FMT_D), +    OPC_CMP_UEQ_D = FOP (51, FMT_D), +    OPC_CMP_OLT_D = FOP (52, FMT_D), +    OPC_CMP_ULT_D = FOP (53, FMT_D), +    OPC_CMP_OLE_D = FOP (54, FMT_D), +    OPC_CMP_ULE_D = FOP (55, FMT_D), +    OPC_CMP_SF_D = FOP (56, FMT_D), +    OPC_CMP_NGLE_D = FOP (57, FMT_D), +    OPC_CMP_SEQ_D = FOP (58, FMT_D), +    OPC_CMP_NGL_D = FOP (59, FMT_D), +    OPC_CMP_LT_D = FOP (60, FMT_D), +    OPC_CMP_NGE_D = FOP (61, FMT_D), +    OPC_CMP_LE_D = FOP (62, FMT_D), +    OPC_CMP_NGT_D = FOP (63, FMT_D), + +    OPC_CVT_S_W = FOP(32, FMT_W), +    OPC_CVT_D_W = FOP(33, FMT_W), +    OPC_CVT_S_L = FOP(32, FMT_L), +    OPC_CVT_D_L = FOP(33, FMT_L), +    OPC_CVT_PS_PW = FOP(38, FMT_W), + +    OPC_ADD_PS = FOP(0, FMT_PS), +    OPC_SUB_PS = FOP(1, FMT_PS), +    OPC_MUL_PS = FOP(2, FMT_PS), +    OPC_DIV_PS = FOP(3, FMT_PS), +    OPC_ABS_PS = FOP(5, FMT_PS), +    OPC_MOV_PS = FOP(6, FMT_PS), +    OPC_NEG_PS = FOP(7, FMT_PS), +    OPC_MOVCF_PS = FOP(17, FMT_PS), +    OPC_MOVZ_PS = FOP(18, FMT_PS), +    OPC_MOVN_PS = FOP(19, FMT_PS), +    OPC_ADDR_PS = FOP(24, FMT_PS), +    OPC_MULR_PS = FOP(26, FMT_PS), +    OPC_RECIP2_PS = FOP(28, FMT_PS), +    OPC_RECIP1_PS = FOP(29, FMT_PS), +    OPC_RSQRT1_PS = FOP(30, FMT_PS), +    OPC_RSQRT2_PS = FOP(31, FMT_PS), + +    OPC_CVT_S_PU = FOP(32, FMT_PS), +    OPC_CVT_PW_PS = FOP(36, FMT_PS), +    OPC_CVT_S_PL = FOP(40, FMT_PS), +    OPC_PLL_PS = FOP(44, FMT_PS), +    OPC_PLU_PS = FOP(45, FMT_PS), +    OPC_PUL_PS = FOP(46, FMT_PS), +    OPC_PUU_PS = FOP(47, FMT_PS), +    OPC_CMP_F_PS = FOP (48, FMT_PS), +    OPC_CMP_UN_PS = FOP (49, FMT_PS), +    OPC_CMP_EQ_PS = FOP (50, FMT_PS), +    OPC_CMP_UEQ_PS = FOP (51, FMT_PS), +    OPC_CMP_OLT_PS = FOP (52, FMT_PS), +    OPC_CMP_ULT_PS = FOP (53, FMT_PS), +    OPC_CMP_OLE_PS = FOP (54, FMT_PS), +    OPC_CMP_ULE_PS = FOP (55, FMT_PS), +    OPC_CMP_SF_PS = FOP (56, FMT_PS), +    OPC_CMP_NGLE_PS = FOP (57, FMT_PS), +    OPC_CMP_SEQ_PS = FOP (58, FMT_PS), +    OPC_CMP_NGL_PS = FOP (59, FMT_PS), +    OPC_CMP_LT_PS = FOP (60, FMT_PS), +    OPC_CMP_NGE_PS = FOP (61, FMT_PS), +    OPC_CMP_LE_PS = FOP (62, FMT_PS), +    OPC_CMP_NGT_PS = FOP (63, FMT_PS), +}; + +enum r6_f_cmp_op { +    R6_OPC_CMP_AF_S   = FOP(0, FMT_W), +    R6_OPC_CMP_UN_S   = FOP(1, FMT_W), +    R6_OPC_CMP_EQ_S   = FOP(2, FMT_W), +    R6_OPC_CMP_UEQ_S  = FOP(3, FMT_W), +    R6_OPC_CMP_LT_S   = FOP(4, FMT_W), +    R6_OPC_CMP_ULT_S  = FOP(5, FMT_W), +    R6_OPC_CMP_LE_S   = FOP(6, FMT_W), +    R6_OPC_CMP_ULE_S  = FOP(7, FMT_W), +    R6_OPC_CMP_SAF_S  = FOP(8, FMT_W), +    R6_OPC_CMP_SUN_S  = FOP(9, FMT_W), +    R6_OPC_CMP_SEQ_S  = FOP(10, FMT_W), +    R6_OPC_CMP_SEUQ_S = FOP(11, FMT_W), +    R6_OPC_CMP_SLT_S  = FOP(12, FMT_W), +    R6_OPC_CMP_SULT_S = FOP(13, FMT_W), +    R6_OPC_CMP_SLE_S  = FOP(14, FMT_W), +    R6_OPC_CMP_SULE_S = FOP(15, FMT_W), +    R6_OPC_CMP_OR_S   = FOP(17, FMT_W), +    R6_OPC_CMP_UNE_S  = FOP(18, FMT_W), +    R6_OPC_CMP_NE_S   = FOP(19, FMT_W), +    R6_OPC_CMP_SOR_S  = FOP(25, FMT_W), +    R6_OPC_CMP_SUNE_S = FOP(26, FMT_W), +    R6_OPC_CMP_SNE_S  = FOP(27, FMT_W), + +    R6_OPC_CMP_AF_D   = FOP(0, FMT_L), +    R6_OPC_CMP_UN_D   = FOP(1, FMT_L), +    R6_OPC_CMP_EQ_D   = FOP(2, FMT_L), +    R6_OPC_CMP_UEQ_D  = FOP(3, FMT_L), +    R6_OPC_CMP_LT_D   = FOP(4, FMT_L), +    R6_OPC_CMP_ULT_D  = FOP(5, FMT_L), +    R6_OPC_CMP_LE_D   = FOP(6, FMT_L), +    R6_OPC_CMP_ULE_D  = FOP(7, FMT_L), +    R6_OPC_CMP_SAF_D  = FOP(8, FMT_L), +    R6_OPC_CMP_SUN_D  = FOP(9, FMT_L), +    R6_OPC_CMP_SEQ_D  = FOP(10, FMT_L), +    R6_OPC_CMP_SEUQ_D = FOP(11, FMT_L), +    R6_OPC_CMP_SLT_D  = FOP(12, FMT_L), +    R6_OPC_CMP_SULT_D = FOP(13, FMT_L), +    R6_OPC_CMP_SLE_D  = FOP(14, FMT_L), +    R6_OPC_CMP_SULE_D = FOP(15, FMT_L), +    R6_OPC_CMP_OR_D   = FOP(17, FMT_L), +    R6_OPC_CMP_UNE_D  = FOP(18, FMT_L), +    R6_OPC_CMP_NE_D   = FOP(19, FMT_L), +    R6_OPC_CMP_SOR_D  = FOP(25, FMT_L), +    R6_OPC_CMP_SUNE_D = FOP(26, FMT_L), +    R6_OPC_CMP_SNE_D  = FOP(27, FMT_L), +}; +static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) +{ +    const char *opn = "cp1 move"; +    TCGv t0 = tcg_temp_new(); + +    switch (opc) { +    case OPC_MFC1: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            tcg_gen_ext_i32_tl(t0, fp0); +            tcg_temp_free_i32(fp0); +        } +        gen_store_gpr(t0, rt); +        opn = "mfc1"; +        break; +    case OPC_MTC1: +        gen_load_gpr(t0, rt); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            tcg_gen_trunc_tl_i32(fp0, t0); +            gen_store_fpr32(ctx, fp0, fs); +            tcg_temp_free_i32(fp0); +        } +        opn = "mtc1"; +        break; +    case OPC_CFC1: +        gen_helper_1e0i(cfc1, t0, fs); +        gen_store_gpr(t0, rt); +        opn = "cfc1"; +        break; +    case OPC_CTC1: +        gen_load_gpr(t0, rt); +        save_cpu_state(ctx, 1); +        { +            TCGv_i32 fs_tmp = tcg_const_i32(fs); + +            gen_helper_0e2i(ctc1, t0, fs_tmp, rt); +            tcg_temp_free_i32(fs_tmp); +        } +        /* Stop translation as we may have changed hflags */ +        ctx->bstate = BS_STOP; +        opn = "ctc1"; +        break; +#if defined(TARGET_MIPS64) +    case OPC_DMFC1: +        gen_load_fpr64(ctx, t0, fs); +        gen_store_gpr(t0, rt); +        opn = "dmfc1"; +        break; +    case OPC_DMTC1: +        gen_load_gpr(t0, rt); +        gen_store_fpr64(ctx, t0, fs); +        opn = "dmtc1"; +        break; +#endif +    case OPC_MFHC1: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32h(ctx, fp0, fs); +            tcg_gen_ext_i32_tl(t0, fp0); +            tcg_temp_free_i32(fp0); +        } +        gen_store_gpr(t0, rt); +        opn = "mfhc1"; +        break; +    case OPC_MTHC1: +        gen_load_gpr(t0, rt); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            tcg_gen_trunc_tl_i32(fp0, t0); +            gen_store_fpr32h(ctx, fp0, fs); +            tcg_temp_free_i32(fp0); +        } +        opn = "mthc1"; +        break; +    default: +        MIPS_INVAL(opn); +        generate_exception (ctx, EXCP_RI); +        goto out; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s %s", opn, regnames[rt], fregnames[fs]); + + out: +    tcg_temp_free(t0); +} + +static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf) +{ +    TCGLabel *l1; +    TCGCond cond; +    TCGv_i32 t0; + +    if (rd == 0) { +        /* Treat as NOP. */ +        return; +    } + +    if (tf) +        cond = TCG_COND_EQ; +    else +        cond = TCG_COND_NE; + +    l1 = gen_new_label(); +    t0 = tcg_temp_new_i32(); +    tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc)); +    tcg_gen_brcondi_i32(cond, t0, 0, l1); +    tcg_temp_free_i32(t0); +    if (rs == 0) { +        tcg_gen_movi_tl(cpu_gpr[rd], 0); +    } else { +        tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]); +    } +    gen_set_label(l1); +} + +static inline void gen_movcf_s(DisasContext *ctx, int fs, int fd, int cc, +                               int tf) +{ +    int cond; +    TCGv_i32 t0 = tcg_temp_new_i32(); +    TCGLabel *l1 = gen_new_label(); + +    if (tf) +        cond = TCG_COND_EQ; +    else +        cond = TCG_COND_NE; + +    tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc)); +    tcg_gen_brcondi_i32(cond, t0, 0, l1); +    gen_load_fpr32(ctx, t0, fs); +    gen_store_fpr32(ctx, t0, fd); +    gen_set_label(l1); +    tcg_temp_free_i32(t0); +} + +static inline void gen_movcf_d (DisasContext *ctx, int fs, int fd, int cc, int tf) +{ +    int cond; +    TCGv_i32 t0 = tcg_temp_new_i32(); +    TCGv_i64 fp0; +    TCGLabel *l1 = gen_new_label(); + +    if (tf) +        cond = TCG_COND_EQ; +    else +        cond = TCG_COND_NE; + +    tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc)); +    tcg_gen_brcondi_i32(cond, t0, 0, l1); +    tcg_temp_free_i32(t0); +    fp0 = tcg_temp_new_i64(); +    gen_load_fpr64(ctx, fp0, fs); +    gen_store_fpr64(ctx, fp0, fd); +    tcg_temp_free_i64(fp0); +    gen_set_label(l1); +} + +static inline void gen_movcf_ps(DisasContext *ctx, int fs, int fd, +                                int cc, int tf) +{ +    int cond; +    TCGv_i32 t0 = tcg_temp_new_i32(); +    TCGLabel *l1 = gen_new_label(); +    TCGLabel *l2 = gen_new_label(); + +    if (tf) +        cond = TCG_COND_EQ; +    else +        cond = TCG_COND_NE; + +    tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc)); +    tcg_gen_brcondi_i32(cond, t0, 0, l1); +    gen_load_fpr32(ctx, t0, fs); +    gen_store_fpr32(ctx, t0, fd); +    gen_set_label(l1); + +    tcg_gen_andi_i32(t0, fpu_fcr31, 1 << get_fp_bit(cc+1)); +    tcg_gen_brcondi_i32(cond, t0, 0, l2); +    gen_load_fpr32h(ctx, t0, fs); +    gen_store_fpr32h(ctx, t0, fd); +    tcg_temp_free_i32(t0); +    gen_set_label(l2); +} + +static void gen_sel_s(DisasContext *ctx, enum fopcode op1, int fd, int ft, +                      int fs) +{ +    TCGv_i32 t1 = tcg_const_i32(0); +    TCGv_i32 fp0 = tcg_temp_new_i32(); +    TCGv_i32 fp1 = tcg_temp_new_i32(); +    TCGv_i32 fp2 = tcg_temp_new_i32(); +    gen_load_fpr32(ctx, fp0, fd); +    gen_load_fpr32(ctx, fp1, ft); +    gen_load_fpr32(ctx, fp2, fs); + +    switch (op1) { +    case OPC_SEL_S: +        tcg_gen_andi_i32(fp0, fp0, 1); +        tcg_gen_movcond_i32(TCG_COND_NE, fp0, fp0, t1, fp1, fp2); +        break; +    case OPC_SELEQZ_S: +        tcg_gen_andi_i32(fp1, fp1, 1); +        tcg_gen_movcond_i32(TCG_COND_EQ, fp0, fp1, t1, fp2, t1); +        break; +    case OPC_SELNEZ_S: +        tcg_gen_andi_i32(fp1, fp1, 1); +        tcg_gen_movcond_i32(TCG_COND_NE, fp0, fp1, t1, fp2, t1); +        break; +    default: +        MIPS_INVAL("gen_sel_s"); +        generate_exception (ctx, EXCP_RI); +        break; +    } + +    gen_store_fpr32(ctx, fp0, fd); +    tcg_temp_free_i32(fp2); +    tcg_temp_free_i32(fp1); +    tcg_temp_free_i32(fp0); +    tcg_temp_free_i32(t1); +} + +static void gen_sel_d(DisasContext *ctx, enum fopcode op1, int fd, int ft, +                      int fs) +{ +    TCGv_i64 t1 = tcg_const_i64(0); +    TCGv_i64 fp0 = tcg_temp_new_i64(); +    TCGv_i64 fp1 = tcg_temp_new_i64(); +    TCGv_i64 fp2 = tcg_temp_new_i64(); +    gen_load_fpr64(ctx, fp0, fd); +    gen_load_fpr64(ctx, fp1, ft); +    gen_load_fpr64(ctx, fp2, fs); + +    switch (op1) { +    case OPC_SEL_D: +        tcg_gen_andi_i64(fp0, fp0, 1); +        tcg_gen_movcond_i64(TCG_COND_NE, fp0, fp0, t1, fp1, fp2); +        break; +    case OPC_SELEQZ_D: +        tcg_gen_andi_i64(fp1, fp1, 1); +        tcg_gen_movcond_i64(TCG_COND_EQ, fp0, fp1, t1, fp2, t1); +        break; +    case OPC_SELNEZ_D: +        tcg_gen_andi_i64(fp1, fp1, 1); +        tcg_gen_movcond_i64(TCG_COND_NE, fp0, fp1, t1, fp2, t1); +        break; +    default: +        MIPS_INVAL("gen_sel_d"); +        generate_exception (ctx, EXCP_RI); +        break; +    } + +    gen_store_fpr64(ctx, fp0, fd); +    tcg_temp_free_i64(fp2); +    tcg_temp_free_i64(fp1); +    tcg_temp_free_i64(fp0); +    tcg_temp_free_i64(t1); +} + +static void gen_farith (DisasContext *ctx, enum fopcode op1, +                        int ft, int fs, int fd, int cc) +{ +    const char *opn = "farith"; +    const char *condnames[] = { +            "c.f", +            "c.un", +            "c.eq", +            "c.ueq", +            "c.olt", +            "c.ult", +            "c.ole", +            "c.ule", +            "c.sf", +            "c.ngle", +            "c.seq", +            "c.ngl", +            "c.lt", +            "c.nge", +            "c.le", +            "c.ngt", +    }; +    const char *condnames_abs[] = { +            "cabs.f", +            "cabs.un", +            "cabs.eq", +            "cabs.ueq", +            "cabs.olt", +            "cabs.ult", +            "cabs.ole", +            "cabs.ule", +            "cabs.sf", +            "cabs.ngle", +            "cabs.seq", +            "cabs.ngl", +            "cabs.lt", +            "cabs.nge", +            "cabs.le", +            "cabs.ngt", +    }; +    enum { BINOP, CMPOP, OTHEROP } optype = OTHEROP; +    uint32_t func = ctx->opcode & 0x3f; +    switch (op1) { +    case OPC_ADD_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_helper_float_add_s(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i32(fp1); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "add.s"; +        optype = BINOP; +        break; +    case OPC_SUB_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_helper_float_sub_s(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i32(fp1); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "sub.s"; +        optype = BINOP; +        break; +    case OPC_MUL_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_helper_float_mul_s(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i32(fp1); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "mul.s"; +        optype = BINOP; +        break; +    case OPC_DIV_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_helper_float_div_s(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i32(fp1); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "div.s"; +        optype = BINOP; +        break; +    case OPC_SQRT_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_sqrt_s(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "sqrt.s"; +        break; +    case OPC_ABS_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_abs_s(fp0, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "abs.s"; +        break; +    case OPC_MOV_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "mov.s"; +        break; +    case OPC_NEG_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_chs_s(fp0, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "neg.s"; +        break; +    case OPC_ROUND_L_S: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr32(ctx, fp32, fs); +            gen_helper_float_roundl_s(fp64, cpu_env, fp32); +            tcg_temp_free_i32(fp32); +            gen_store_fpr64(ctx, fp64, fd); +            tcg_temp_free_i64(fp64); +        } +        opn = "round.l.s"; +        break; +    case OPC_TRUNC_L_S: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr32(ctx, fp32, fs); +            gen_helper_float_truncl_s(fp64, cpu_env, fp32); +            tcg_temp_free_i32(fp32); +            gen_store_fpr64(ctx, fp64, fd); +            tcg_temp_free_i64(fp64); +        } +        opn = "trunc.l.s"; +        break; +    case OPC_CEIL_L_S: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr32(ctx, fp32, fs); +            gen_helper_float_ceill_s(fp64, cpu_env, fp32); +            tcg_temp_free_i32(fp32); +            gen_store_fpr64(ctx, fp64, fd); +            tcg_temp_free_i64(fp64); +        } +        opn = "ceil.l.s"; +        break; +    case OPC_FLOOR_L_S: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr32(ctx, fp32, fs); +            gen_helper_float_floorl_s(fp64, cpu_env, fp32); +            tcg_temp_free_i32(fp32); +            gen_store_fpr64(ctx, fp64, fd); +            tcg_temp_free_i64(fp64); +        } +        opn = "floor.l.s"; +        break; +    case OPC_ROUND_W_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_roundw_s(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "round.w.s"; +        break; +    case OPC_TRUNC_W_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_truncw_s(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "trunc.w.s"; +        break; +    case OPC_CEIL_W_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_ceilw_s(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "ceil.w.s"; +        break; +    case OPC_FLOOR_W_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_floorw_s(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "floor.w.s"; +        break; +    case OPC_SEL_S: +        check_insn(ctx, ISA_MIPS32R6); +        gen_sel_s(ctx, op1, fd, ft, fs); +        opn = "sel.s"; +        break; +    case OPC_SELEQZ_S: +        check_insn(ctx, ISA_MIPS32R6); +        gen_sel_s(ctx, op1, fd, ft, fs); +        opn = "seleqz.s"; +        break; +    case OPC_SELNEZ_S: +        check_insn(ctx, ISA_MIPS32R6); +        gen_sel_s(ctx, op1, fd, ft, fs); +        opn = "selnez.s"; +        break; +    case OPC_MOVCF_S: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        gen_movcf_s(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1); +        opn = "movcf.s"; +        break; +    case OPC_MOVZ_S: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        { +            TCGLabel *l1 = gen_new_label(); +            TCGv_i32 fp0; + +            if (ft != 0) { +                tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[ft], 0, l1); +            } +            fp0 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, fs); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +            gen_set_label(l1); +        } +        opn = "movz.s"; +        break; +    case OPC_MOVN_S: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        { +            TCGLabel *l1 = gen_new_label(); +            TCGv_i32 fp0; + +            if (ft != 0) { +                tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[ft], 0, l1); +                fp0 = tcg_temp_new_i32(); +                gen_load_fpr32(ctx, fp0, fs); +                gen_store_fpr32(ctx, fp0, fd); +                tcg_temp_free_i32(fp0); +                gen_set_label(l1); +            } +        } +        opn = "movn.s"; +        break; +    case OPC_RECIP_S: +        check_cop1x(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_recip_s(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "recip.s"; +        break; +    case OPC_RSQRT_S: +        check_cop1x(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_rsqrt_s(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "rsqrt.s"; +        break; +    case OPC_MADDF_S: +        check_insn(ctx, ISA_MIPS32R6); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); +            TCGv_i32 fp2 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_load_fpr32(ctx, fp2, fd); +            gen_helper_float_maddf_s(fp2, cpu_env, fp0, fp1, fp2); +            gen_store_fpr32(ctx, fp2, fd); +            tcg_temp_free_i32(fp2); +            tcg_temp_free_i32(fp1); +            tcg_temp_free_i32(fp0); +            opn = "maddf.s"; +        } +        break; +    case OPC_MSUBF_S: +        check_insn(ctx, ISA_MIPS32R6); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); +            TCGv_i32 fp2 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_load_fpr32(ctx, fp2, fd); +            gen_helper_float_msubf_s(fp2, cpu_env, fp0, fp1, fp2); +            gen_store_fpr32(ctx, fp2, fd); +            tcg_temp_free_i32(fp2); +            tcg_temp_free_i32(fp1); +            tcg_temp_free_i32(fp0); +            opn = "msubf.s"; +        } +        break; +    case OPC_RINT_S: +        check_insn(ctx, ISA_MIPS32R6); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_rint_s(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +            opn = "rint.s"; +        } +        break; +    case OPC_CLASS_S: +        check_insn(ctx, ISA_MIPS32R6); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_class_s(fp0, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +            opn = "class.s"; +        } +        break; +    case OPC_MIN_S: /* OPC_RECIP2_S */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_MIN_S */ +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); +            TCGv_i32 fp2 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_helper_float_min_s(fp2, cpu_env, fp0, fp1); +            gen_store_fpr32(ctx, fp2, fd); +            tcg_temp_free_i32(fp2); +            tcg_temp_free_i32(fp1); +            tcg_temp_free_i32(fp0); +            opn = "min.s"; +        } else { +            /* OPC_RECIP2_S */ +            check_cp1_64bitmode(ctx); +            { +                TCGv_i32 fp0 = tcg_temp_new_i32(); +                TCGv_i32 fp1 = tcg_temp_new_i32(); + +                gen_load_fpr32(ctx, fp0, fs); +                gen_load_fpr32(ctx, fp1, ft); +                gen_helper_float_recip2_s(fp0, cpu_env, fp0, fp1); +                tcg_temp_free_i32(fp1); +                gen_store_fpr32(ctx, fp0, fd); +                tcg_temp_free_i32(fp0); +            } +            opn = "recip2.s"; +        } +        break; +    case OPC_MINA_S: /* OPC_RECIP1_S */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_MINA_S */ +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); +            TCGv_i32 fp2 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_helper_float_mina_s(fp2, cpu_env, fp0, fp1); +            gen_store_fpr32(ctx, fp2, fd); +            tcg_temp_free_i32(fp2); +            tcg_temp_free_i32(fp1); +            tcg_temp_free_i32(fp0); +            opn = "mina.s"; +        } else { +            /* OPC_RECIP1_S */ +            check_cp1_64bitmode(ctx); +            { +                TCGv_i32 fp0 = tcg_temp_new_i32(); + +                gen_load_fpr32(ctx, fp0, fs); +                gen_helper_float_recip1_s(fp0, cpu_env, fp0); +                gen_store_fpr32(ctx, fp0, fd); +                tcg_temp_free_i32(fp0); +            } +            opn = "recip1.s"; +        } +        break; +    case OPC_MAX_S: /* OPC_RSQRT1_S */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_MAX_S */ +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_helper_float_max_s(fp1, cpu_env, fp0, fp1); +            gen_store_fpr32(ctx, fp1, fd); +            tcg_temp_free_i32(fp1); +            tcg_temp_free_i32(fp0); +            opn = "max.s"; +        } else { +            /* OPC_RSQRT1_S */ +            check_cp1_64bitmode(ctx); +            { +                TCGv_i32 fp0 = tcg_temp_new_i32(); + +                gen_load_fpr32(ctx, fp0, fs); +                gen_helper_float_rsqrt1_s(fp0, cpu_env, fp0); +                gen_store_fpr32(ctx, fp0, fd); +                tcg_temp_free_i32(fp0); +            } +            opn = "rsqrt1.s"; +        } +        break; +    case OPC_MAXA_S: /* OPC_RSQRT2_S */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_MAXA_S */ +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_helper_float_maxa_s(fp1, cpu_env, fp0, fp1); +            gen_store_fpr32(ctx, fp1, fd); +            tcg_temp_free_i32(fp1); +            tcg_temp_free_i32(fp0); +            opn = "maxa.s"; +        } else { +            /* OPC_RSQRT2_S */ +            check_cp1_64bitmode(ctx); +            { +                TCGv_i32 fp0 = tcg_temp_new_i32(); +                TCGv_i32 fp1 = tcg_temp_new_i32(); + +                gen_load_fpr32(ctx, fp0, fs); +                gen_load_fpr32(ctx, fp1, ft); +                gen_helper_float_rsqrt2_s(fp0, cpu_env, fp0, fp1); +                tcg_temp_free_i32(fp1); +                gen_store_fpr32(ctx, fp0, fd); +                tcg_temp_free_i32(fp0); +            } +            opn = "rsqrt2.s"; +        } +        break; +    case OPC_CVT_D_S: +        check_cp1_registers(ctx, fd); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr32(ctx, fp32, fs); +            gen_helper_float_cvtd_s(fp64, cpu_env, fp32); +            tcg_temp_free_i32(fp32); +            gen_store_fpr64(ctx, fp64, fd); +            tcg_temp_free_i64(fp64); +        } +        opn = "cvt.d.s"; +        break; +    case OPC_CVT_W_S: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_cvtw_s(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "cvt.w.s"; +        break; +    case OPC_CVT_L_S: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr32(ctx, fp32, fs); +            gen_helper_float_cvtl_s(fp64, cpu_env, fp32); +            tcg_temp_free_i32(fp32); +            gen_store_fpr64(ctx, fp64, fd); +            tcg_temp_free_i64(fp64); +        } +        opn = "cvt.l.s"; +        break; +    case OPC_CVT_PS_S: +        check_ps(ctx); +        { +            TCGv_i64 fp64 = tcg_temp_new_i64(); +            TCGv_i32 fp32_0 = tcg_temp_new_i32(); +            TCGv_i32 fp32_1 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp32_0, fs); +            gen_load_fpr32(ctx, fp32_1, ft); +            tcg_gen_concat_i32_i64(fp64, fp32_1, fp32_0); +            tcg_temp_free_i32(fp32_1); +            tcg_temp_free_i32(fp32_0); +            gen_store_fpr64(ctx, fp64, fd); +            tcg_temp_free_i64(fp64); +        } +        opn = "cvt.ps.s"; +        break; +    case OPC_CMP_F_S: +    case OPC_CMP_UN_S: +    case OPC_CMP_EQ_S: +    case OPC_CMP_UEQ_S: +    case OPC_CMP_OLT_S: +    case OPC_CMP_ULT_S: +    case OPC_CMP_OLE_S: +    case OPC_CMP_ULE_S: +    case OPC_CMP_SF_S: +    case OPC_CMP_NGLE_S: +    case OPC_CMP_SEQ_S: +    case OPC_CMP_NGL_S: +    case OPC_CMP_LT_S: +    case OPC_CMP_NGE_S: +    case OPC_CMP_LE_S: +    case OPC_CMP_NGT_S: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        if (ctx->opcode & (1 << 6)) { +            gen_cmpabs_s(ctx, func-48, ft, fs, cc); +            opn = condnames_abs[func-48]; +        } else { +            gen_cmp_s(ctx, func-48, ft, fs, cc); +            opn = condnames[func-48]; +        } +        optype = CMPOP; +        break; +    case OPC_ADD_D: +        check_cp1_registers(ctx, fs | ft | fd); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_add_d(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "add.d"; +        optype = BINOP; +        break; +    case OPC_SUB_D: +        check_cp1_registers(ctx, fs | ft | fd); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_sub_d(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "sub.d"; +        optype = BINOP; +        break; +    case OPC_MUL_D: +        check_cp1_registers(ctx, fs | ft | fd); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_mul_d(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "mul.d"; +        optype = BINOP; +        break; +    case OPC_DIV_D: +        check_cp1_registers(ctx, fs | ft | fd); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_div_d(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "div.d"; +        optype = BINOP; +        break; +    case OPC_SQRT_D: +        check_cp1_registers(ctx, fs | fd); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_sqrt_d(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "sqrt.d"; +        break; +    case OPC_ABS_D: +        check_cp1_registers(ctx, fs | fd); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_abs_d(fp0, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "abs.d"; +        break; +    case OPC_MOV_D: +        check_cp1_registers(ctx, fs | fd); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "mov.d"; +        break; +    case OPC_NEG_D: +        check_cp1_registers(ctx, fs | fd); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_chs_d(fp0, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "neg.d"; +        break; +    case OPC_ROUND_L_D: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_roundl_d(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "round.l.d"; +        break; +    case OPC_TRUNC_L_D: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_truncl_d(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "trunc.l.d"; +        break; +    case OPC_CEIL_L_D: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_ceill_d(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "ceil.l.d"; +        break; +    case OPC_FLOOR_L_D: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_floorl_d(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "floor.l.d"; +        break; +    case OPC_ROUND_W_D: +        check_cp1_registers(ctx, fs); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp64, fs); +            gen_helper_float_roundw_d(fp32, cpu_env, fp64); +            tcg_temp_free_i64(fp64); +            gen_store_fpr32(ctx, fp32, fd); +            tcg_temp_free_i32(fp32); +        } +        opn = "round.w.d"; +        break; +    case OPC_TRUNC_W_D: +        check_cp1_registers(ctx, fs); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp64, fs); +            gen_helper_float_truncw_d(fp32, cpu_env, fp64); +            tcg_temp_free_i64(fp64); +            gen_store_fpr32(ctx, fp32, fd); +            tcg_temp_free_i32(fp32); +        } +        opn = "trunc.w.d"; +        break; +    case OPC_CEIL_W_D: +        check_cp1_registers(ctx, fs); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp64, fs); +            gen_helper_float_ceilw_d(fp32, cpu_env, fp64); +            tcg_temp_free_i64(fp64); +            gen_store_fpr32(ctx, fp32, fd); +            tcg_temp_free_i32(fp32); +        } +        opn = "ceil.w.d"; +        break; +    case OPC_FLOOR_W_D: +        check_cp1_registers(ctx, fs); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp64, fs); +            gen_helper_float_floorw_d(fp32, cpu_env, fp64); +            tcg_temp_free_i64(fp64); +            gen_store_fpr32(ctx, fp32, fd); +            tcg_temp_free_i32(fp32); +        } +        opn = "floor.w.d"; +        break; +    case OPC_SEL_D: +        check_insn(ctx, ISA_MIPS32R6); +        gen_sel_d(ctx, op1, fd, ft, fs); +        opn = "sel.d"; +        break; +    case OPC_SELEQZ_D: +        check_insn(ctx, ISA_MIPS32R6); +        gen_sel_d(ctx, op1, fd, ft, fs); +        opn = "seleqz.d"; +        break; +    case OPC_SELNEZ_D: +        check_insn(ctx, ISA_MIPS32R6); +        gen_sel_d(ctx, op1, fd, ft, fs); +        opn = "selnez.d"; +        break; +    case OPC_MOVCF_D: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        gen_movcf_d(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1); +        opn = "movcf.d"; +        break; +    case OPC_MOVZ_D: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        { +            TCGLabel *l1 = gen_new_label(); +            TCGv_i64 fp0; + +            if (ft != 0) { +                tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[ft], 0, l1); +            } +            fp0 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +            gen_set_label(l1); +        } +        opn = "movz.d"; +        break; +    case OPC_MOVN_D: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        { +            TCGLabel *l1 = gen_new_label(); +            TCGv_i64 fp0; + +            if (ft != 0) { +                tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[ft], 0, l1); +                fp0 = tcg_temp_new_i64(); +                gen_load_fpr64(ctx, fp0, fs); +                gen_store_fpr64(ctx, fp0, fd); +                tcg_temp_free_i64(fp0); +                gen_set_label(l1); +            } +        } +        opn = "movn.d"; +        break; +    case OPC_RECIP_D: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_recip_d(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "recip.d"; +        break; +    case OPC_RSQRT_D: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_rsqrt_d(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "rsqrt.d"; +        break; +    case OPC_MADDF_D: +        check_insn(ctx, ISA_MIPS32R6); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            TCGv_i64 fp2 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_load_fpr64(ctx, fp2, fd); +            gen_helper_float_maddf_d(fp2, cpu_env, fp0, fp1, fp2); +            gen_store_fpr64(ctx, fp2, fd); +            tcg_temp_free_i64(fp2); +            tcg_temp_free_i64(fp1); +            tcg_temp_free_i64(fp0); +            opn = "maddf.d"; +        } +        break; +    case OPC_MSUBF_D: +        check_insn(ctx, ISA_MIPS32R6); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            TCGv_i64 fp2 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_load_fpr64(ctx, fp2, fd); +            gen_helper_float_msubf_d(fp2, cpu_env, fp0, fp1, fp2); +            gen_store_fpr64(ctx, fp2, fd); +            tcg_temp_free_i64(fp2); +            tcg_temp_free_i64(fp1); +            tcg_temp_free_i64(fp0); +            opn = "msubf.d"; +        } +        break; +    case OPC_RINT_D: +        check_insn(ctx, ISA_MIPS32R6); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_rint_d(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +            opn = "rint.d"; +        } +        break; +    case OPC_CLASS_D: +        check_insn(ctx, ISA_MIPS32R6); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_class_d(fp0, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +            opn = "class.d"; +        } +        break; +    case OPC_MIN_D: /* OPC_RECIP2_D */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_MIN_D */ +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_min_d(fp1, cpu_env, fp0, fp1); +            gen_store_fpr64(ctx, fp1, fd); +            tcg_temp_free_i64(fp1); +            tcg_temp_free_i64(fp0); +            opn = "min.d"; +        } else { +            /* OPC_RECIP2_D */ +            check_cp1_64bitmode(ctx); +            { +                TCGv_i64 fp0 = tcg_temp_new_i64(); +                TCGv_i64 fp1 = tcg_temp_new_i64(); + +                gen_load_fpr64(ctx, fp0, fs); +                gen_load_fpr64(ctx, fp1, ft); +                gen_helper_float_recip2_d(fp0, cpu_env, fp0, fp1); +                tcg_temp_free_i64(fp1); +                gen_store_fpr64(ctx, fp0, fd); +                tcg_temp_free_i64(fp0); +            } +            opn = "recip2.d"; +        } +        break; +    case OPC_MINA_D: /* OPC_RECIP1_D */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_MINA_D */ +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_mina_d(fp1, cpu_env, fp0, fp1); +            gen_store_fpr64(ctx, fp1, fd); +            tcg_temp_free_i64(fp1); +            tcg_temp_free_i64(fp0); +            opn = "mina.d"; +        } else { +            /* OPC_RECIP1_D */ +            check_cp1_64bitmode(ctx); +            { +                TCGv_i64 fp0 = tcg_temp_new_i64(); + +                gen_load_fpr64(ctx, fp0, fs); +                gen_helper_float_recip1_d(fp0, cpu_env, fp0); +                gen_store_fpr64(ctx, fp0, fd); +                tcg_temp_free_i64(fp0); +            } +            opn = "recip1.d"; +        } +        break; +    case OPC_MAX_D: /*  OPC_RSQRT1_D */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_MAX_D */ +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_max_d(fp1, cpu_env, fp0, fp1); +            gen_store_fpr64(ctx, fp1, fd); +            tcg_temp_free_i64(fp1); +            tcg_temp_free_i64(fp0); +            opn = "max.d"; +        } else { +            /* OPC_RSQRT1_D */ +            check_cp1_64bitmode(ctx); +            { +                TCGv_i64 fp0 = tcg_temp_new_i64(); + +                gen_load_fpr64(ctx, fp0, fs); +                gen_helper_float_rsqrt1_d(fp0, cpu_env, fp0); +                gen_store_fpr64(ctx, fp0, fd); +                tcg_temp_free_i64(fp0); +            } +            opn = "rsqrt1.d"; +        } +        break; +    case OPC_MAXA_D: /* OPC_RSQRT2_D */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_MAXA_D */ +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_maxa_d(fp1, cpu_env, fp0, fp1); +            gen_store_fpr64(ctx, fp1, fd); +            tcg_temp_free_i64(fp1); +            tcg_temp_free_i64(fp0); +            opn = "maxa.d"; +        } else { +            /* OPC_RSQRT2_D */ +            check_cp1_64bitmode(ctx); +            { +                TCGv_i64 fp0 = tcg_temp_new_i64(); +                TCGv_i64 fp1 = tcg_temp_new_i64(); + +                gen_load_fpr64(ctx, fp0, fs); +                gen_load_fpr64(ctx, fp1, ft); +                gen_helper_float_rsqrt2_d(fp0, cpu_env, fp0, fp1); +                tcg_temp_free_i64(fp1); +                gen_store_fpr64(ctx, fp0, fd); +                tcg_temp_free_i64(fp0); +            } +            opn = "rsqrt2.d"; +        } +        break; +    case OPC_CMP_F_D: +    case OPC_CMP_UN_D: +    case OPC_CMP_EQ_D: +    case OPC_CMP_UEQ_D: +    case OPC_CMP_OLT_D: +    case OPC_CMP_ULT_D: +    case OPC_CMP_OLE_D: +    case OPC_CMP_ULE_D: +    case OPC_CMP_SF_D: +    case OPC_CMP_NGLE_D: +    case OPC_CMP_SEQ_D: +    case OPC_CMP_NGL_D: +    case OPC_CMP_LT_D: +    case OPC_CMP_NGE_D: +    case OPC_CMP_LE_D: +    case OPC_CMP_NGT_D: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        if (ctx->opcode & (1 << 6)) { +            gen_cmpabs_d(ctx, func-48, ft, fs, cc); +            opn = condnames_abs[func-48]; +        } else { +            gen_cmp_d(ctx, func-48, ft, fs, cc); +            opn = condnames[func-48]; +        } +        optype = CMPOP; +        break; +    case OPC_CVT_S_D: +        check_cp1_registers(ctx, fs); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp64, fs); +            gen_helper_float_cvts_d(fp32, cpu_env, fp64); +            tcg_temp_free_i64(fp64); +            gen_store_fpr32(ctx, fp32, fd); +            tcg_temp_free_i32(fp32); +        } +        opn = "cvt.s.d"; +        break; +    case OPC_CVT_W_D: +        check_cp1_registers(ctx, fs); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp64, fs); +            gen_helper_float_cvtw_d(fp32, cpu_env, fp64); +            tcg_temp_free_i64(fp64); +            gen_store_fpr32(ctx, fp32, fd); +            tcg_temp_free_i32(fp32); +        } +        opn = "cvt.w.d"; +        break; +    case OPC_CVT_L_D: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_cvtl_d(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "cvt.l.d"; +        break; +    case OPC_CVT_S_W: +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_cvts_w(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "cvt.s.w"; +        break; +    case OPC_CVT_D_W: +        check_cp1_registers(ctx, fd); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr32(ctx, fp32, fs); +            gen_helper_float_cvtd_w(fp64, cpu_env, fp32); +            tcg_temp_free_i32(fp32); +            gen_store_fpr64(ctx, fp64, fd); +            tcg_temp_free_i64(fp64); +        } +        opn = "cvt.d.w"; +        break; +    case OPC_CVT_S_L: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i32 fp32 = tcg_temp_new_i32(); +            TCGv_i64 fp64 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp64, fs); +            gen_helper_float_cvts_l(fp32, cpu_env, fp64); +            tcg_temp_free_i64(fp64); +            gen_store_fpr32(ctx, fp32, fd); +            tcg_temp_free_i32(fp32); +        } +        opn = "cvt.s.l"; +        break; +    case OPC_CVT_D_L: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_cvtd_l(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "cvt.d.l"; +        break; +    case OPC_CVT_PS_PW: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_cvtps_pw(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "cvt.ps.pw"; +        break; +    case OPC_ADD_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_add_ps(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "add.ps"; +        break; +    case OPC_SUB_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_sub_ps(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "sub.ps"; +        break; +    case OPC_MUL_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_mul_ps(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "mul.ps"; +        break; +    case OPC_ABS_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_abs_ps(fp0, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "abs.ps"; +        break; +    case OPC_MOV_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "mov.ps"; +        break; +    case OPC_NEG_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_chs_ps(fp0, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "neg.ps"; +        break; +    case OPC_MOVCF_PS: +        check_ps(ctx); +        gen_movcf_ps(ctx, fs, fd, (ft >> 2) & 0x7, ft & 0x1); +        opn = "movcf.ps"; +        break; +    case OPC_MOVZ_PS: +        check_ps(ctx); +        { +            TCGLabel *l1 = gen_new_label(); +            TCGv_i64 fp0; + +            if (ft != 0) +                tcg_gen_brcondi_tl(TCG_COND_NE, cpu_gpr[ft], 0, l1); +            fp0 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +            gen_set_label(l1); +        } +        opn = "movz.ps"; +        break; +    case OPC_MOVN_PS: +        check_ps(ctx); +        { +            TCGLabel *l1 = gen_new_label(); +            TCGv_i64 fp0; + +            if (ft != 0) { +                tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_gpr[ft], 0, l1); +                fp0 = tcg_temp_new_i64(); +                gen_load_fpr64(ctx, fp0, fs); +                gen_store_fpr64(ctx, fp0, fd); +                tcg_temp_free_i64(fp0); +                gen_set_label(l1); +            } +        } +        opn = "movn.ps"; +        break; +    case OPC_ADDR_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, ft); +            gen_load_fpr64(ctx, fp1, fs); +            gen_helper_float_addr_ps(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "addr.ps"; +        break; +    case OPC_MULR_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, ft); +            gen_load_fpr64(ctx, fp1, fs); +            gen_helper_float_mulr_ps(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "mulr.ps"; +        break; +    case OPC_RECIP2_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_recip2_ps(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "recip2.ps"; +        break; +    case OPC_RECIP1_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_recip1_ps(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "recip1.ps"; +        break; +    case OPC_RSQRT1_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_rsqrt1_ps(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "rsqrt1.ps"; +        break; +    case OPC_RSQRT2_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_helper_float_rsqrt2_ps(fp0, cpu_env, fp0, fp1); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "rsqrt2.ps"; +        break; +    case OPC_CVT_S_PU: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32h(ctx, fp0, fs); +            gen_helper_float_cvts_pu(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "cvt.s.pu"; +        break; +    case OPC_CVT_PW_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_helper_float_cvtpw_ps(fp0, cpu_env, fp0); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "cvt.pw.ps"; +        break; +    case OPC_CVT_S_PL: +        check_cp1_64bitmode(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_helper_float_cvts_pl(fp0, cpu_env, fp0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "cvt.s.pl"; +        break; +    case OPC_PLL_PS: +        check_ps(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_store_fpr32h(ctx, fp0, fd); +            gen_store_fpr32(ctx, fp1, fd); +            tcg_temp_free_i32(fp0); +            tcg_temp_free_i32(fp1); +        } +        opn = "pll.ps"; +        break; +    case OPC_PLU_PS: +        check_ps(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32h(ctx, fp1, ft); +            gen_store_fpr32(ctx, fp1, fd); +            gen_store_fpr32h(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +            tcg_temp_free_i32(fp1); +        } +        opn = "plu.ps"; +        break; +    case OPC_PUL_PS: +        check_ps(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); + +            gen_load_fpr32h(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_store_fpr32(ctx, fp1, fd); +            gen_store_fpr32h(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +            tcg_temp_free_i32(fp1); +        } +        opn = "pul.ps"; +        break; +    case OPC_PUU_PS: +        check_ps(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); + +            gen_load_fpr32h(ctx, fp0, fs); +            gen_load_fpr32h(ctx, fp1, ft); +            gen_store_fpr32(ctx, fp1, fd); +            gen_store_fpr32h(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +            tcg_temp_free_i32(fp1); +        } +        opn = "puu.ps"; +        break; +    case OPC_CMP_F_PS: +    case OPC_CMP_UN_PS: +    case OPC_CMP_EQ_PS: +    case OPC_CMP_UEQ_PS: +    case OPC_CMP_OLT_PS: +    case OPC_CMP_ULT_PS: +    case OPC_CMP_OLE_PS: +    case OPC_CMP_ULE_PS: +    case OPC_CMP_SF_PS: +    case OPC_CMP_NGLE_PS: +    case OPC_CMP_SEQ_PS: +    case OPC_CMP_NGL_PS: +    case OPC_CMP_LT_PS: +    case OPC_CMP_NGE_PS: +    case OPC_CMP_LE_PS: +    case OPC_CMP_NGT_PS: +        if (ctx->opcode & (1 << 6)) { +            gen_cmpabs_ps(ctx, func-48, ft, fs, cc); +            opn = condnames_abs[func-48]; +        } else { +            gen_cmp_ps(ctx, func-48, ft, fs, cc); +            opn = condnames[func-48]; +        } +        optype = CMPOP; +        break; +    default: +        MIPS_INVAL(opn); +        generate_exception (ctx, EXCP_RI); +        return; +    } +    (void)opn; /* avoid a compiler warning */ +    switch (optype) { +    case BINOP: +        MIPS_DEBUG("%s %s, %s, %s", opn, fregnames[fd], fregnames[fs], fregnames[ft]); +        break; +    case CMPOP: +        MIPS_DEBUG("%s %s,%s", opn, fregnames[fs], fregnames[ft]); +        break; +    default: +        MIPS_DEBUG("%s %s,%s", opn, fregnames[fd], fregnames[fs]); +        break; +    } +} + +/* Coprocessor 3 (FPU) */ +static void gen_flt3_ldst (DisasContext *ctx, uint32_t opc, +                           int fd, int fs, int base, int index) +{ +    const char *opn = "extended float load/store"; +    int store = 0; +    TCGv t0 = tcg_temp_new(); + +    if (base == 0) { +        gen_load_gpr(t0, index); +    } else if (index == 0) { +        gen_load_gpr(t0, base); +    } else { +        gen_op_addr_add(ctx, t0, cpu_gpr[base], cpu_gpr[index]); +    } +    /* Don't do NOP if destination is zero: we must perform the actual +       memory access. */ +    switch (opc) { +    case OPC_LWXC1: +        check_cop1x(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); + +            tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL); +            tcg_gen_trunc_tl_i32(fp0, t0); +            gen_store_fpr32(ctx, fp0, fd); +            tcg_temp_free_i32(fp0); +        } +        opn = "lwxc1"; +        break; +    case OPC_LDXC1: +        check_cop1x(ctx); +        check_cp1_registers(ctx, fd); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEQ); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "ldxc1"; +        break; +    case OPC_LUXC1: +        check_cp1_64bitmode(ctx); +        tcg_gen_andi_tl(t0, t0, ~0x7); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); + +            tcg_gen_qemu_ld_i64(fp0, t0, ctx->mem_idx, MO_TEQ); +            gen_store_fpr64(ctx, fp0, fd); +            tcg_temp_free_i64(fp0); +        } +        opn = "luxc1"; +        break; +    case OPC_SWXC1: +        check_cop1x(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            gen_load_fpr32(ctx, fp0, fs); +            tcg_gen_qemu_st_i32(fp0, t0, ctx->mem_idx, MO_TEUL); +            tcg_temp_free_i32(fp0); +        } +        opn = "swxc1"; +        store = 1; +        break; +    case OPC_SDXC1: +        check_cop1x(ctx); +        check_cp1_registers(ctx, fs); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ); +            tcg_temp_free_i64(fp0); +        } +        opn = "sdxc1"; +        store = 1; +        break; +    case OPC_SUXC1: +        check_cp1_64bitmode(ctx); +        tcg_gen_andi_tl(t0, t0, ~0x7); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            gen_load_fpr64(ctx, fp0, fs); +            tcg_gen_qemu_st_i64(fp0, t0, ctx->mem_idx, MO_TEQ); +            tcg_temp_free_i64(fp0); +        } +        opn = "suxc1"; +        store = 1; +        break; +    } +    tcg_temp_free(t0); +    (void)opn; (void)store; /* avoid compiler warnings */ +    MIPS_DEBUG("%s %s, %s(%s)", opn, fregnames[store ? fs : fd], +               regnames[index], regnames[base]); +} + +static void gen_flt3_arith (DisasContext *ctx, uint32_t opc, +                            int fd, int fr, int fs, int ft) +{ +    const char *opn = "flt3_arith"; + +    switch (opc) { +    case OPC_ALNV_PS: +        check_ps(ctx); +        { +            TCGv t0 = tcg_temp_local_new(); +            TCGv_i32 fp = tcg_temp_new_i32(); +            TCGv_i32 fph = tcg_temp_new_i32(); +            TCGLabel *l1 = gen_new_label(); +            TCGLabel *l2 = gen_new_label(); + +            gen_load_gpr(t0, fr); +            tcg_gen_andi_tl(t0, t0, 0x7); + +            tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0, l1); +            gen_load_fpr32(ctx, fp, fs); +            gen_load_fpr32h(ctx, fph, fs); +            gen_store_fpr32(ctx, fp, fd); +            gen_store_fpr32h(ctx, fph, fd); +            tcg_gen_br(l2); +            gen_set_label(l1); +            tcg_gen_brcondi_tl(TCG_COND_NE, t0, 4, l2); +            tcg_temp_free(t0); +#ifdef TARGET_WORDS_BIGENDIAN +            gen_load_fpr32(ctx, fp, fs); +            gen_load_fpr32h(ctx, fph, ft); +            gen_store_fpr32h(ctx, fp, fd); +            gen_store_fpr32(ctx, fph, fd); +#else +            gen_load_fpr32h(ctx, fph, fs); +            gen_load_fpr32(ctx, fp, ft); +            gen_store_fpr32(ctx, fph, fd); +            gen_store_fpr32h(ctx, fp, fd); +#endif +            gen_set_label(l2); +            tcg_temp_free_i32(fp); +            tcg_temp_free_i32(fph); +        } +        opn = "alnv.ps"; +        break; +    case OPC_MADD_S: +        check_cop1x(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); +            TCGv_i32 fp2 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_load_fpr32(ctx, fp2, fr); +            gen_helper_float_madd_s(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i32(fp0); +            tcg_temp_free_i32(fp1); +            gen_store_fpr32(ctx, fp2, fd); +            tcg_temp_free_i32(fp2); +        } +        opn = "madd.s"; +        break; +    case OPC_MADD_D: +        check_cop1x(ctx); +        check_cp1_registers(ctx, fd | fs | ft | fr); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            TCGv_i64 fp2 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_load_fpr64(ctx, fp2, fr); +            gen_helper_float_madd_d(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i64(fp0); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp2, fd); +            tcg_temp_free_i64(fp2); +        } +        opn = "madd.d"; +        break; +    case OPC_MADD_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            TCGv_i64 fp2 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_load_fpr64(ctx, fp2, fr); +            gen_helper_float_madd_ps(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i64(fp0); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp2, fd); +            tcg_temp_free_i64(fp2); +        } +        opn = "madd.ps"; +        break; +    case OPC_MSUB_S: +        check_cop1x(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); +            TCGv_i32 fp2 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_load_fpr32(ctx, fp2, fr); +            gen_helper_float_msub_s(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i32(fp0); +            tcg_temp_free_i32(fp1); +            gen_store_fpr32(ctx, fp2, fd); +            tcg_temp_free_i32(fp2); +        } +        opn = "msub.s"; +        break; +    case OPC_MSUB_D: +        check_cop1x(ctx); +        check_cp1_registers(ctx, fd | fs | ft | fr); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            TCGv_i64 fp2 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_load_fpr64(ctx, fp2, fr); +            gen_helper_float_msub_d(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i64(fp0); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp2, fd); +            tcg_temp_free_i64(fp2); +        } +        opn = "msub.d"; +        break; +    case OPC_MSUB_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            TCGv_i64 fp2 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_load_fpr64(ctx, fp2, fr); +            gen_helper_float_msub_ps(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i64(fp0); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp2, fd); +            tcg_temp_free_i64(fp2); +        } +        opn = "msub.ps"; +        break; +    case OPC_NMADD_S: +        check_cop1x(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); +            TCGv_i32 fp2 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_load_fpr32(ctx, fp2, fr); +            gen_helper_float_nmadd_s(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i32(fp0); +            tcg_temp_free_i32(fp1); +            gen_store_fpr32(ctx, fp2, fd); +            tcg_temp_free_i32(fp2); +        } +        opn = "nmadd.s"; +        break; +    case OPC_NMADD_D: +        check_cop1x(ctx); +        check_cp1_registers(ctx, fd | fs | ft | fr); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            TCGv_i64 fp2 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_load_fpr64(ctx, fp2, fr); +            gen_helper_float_nmadd_d(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i64(fp0); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp2, fd); +            tcg_temp_free_i64(fp2); +        } +        opn = "nmadd.d"; +        break; +    case OPC_NMADD_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            TCGv_i64 fp2 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_load_fpr64(ctx, fp2, fr); +            gen_helper_float_nmadd_ps(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i64(fp0); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp2, fd); +            tcg_temp_free_i64(fp2); +        } +        opn = "nmadd.ps"; +        break; +    case OPC_NMSUB_S: +        check_cop1x(ctx); +        { +            TCGv_i32 fp0 = tcg_temp_new_i32(); +            TCGv_i32 fp1 = tcg_temp_new_i32(); +            TCGv_i32 fp2 = tcg_temp_new_i32(); + +            gen_load_fpr32(ctx, fp0, fs); +            gen_load_fpr32(ctx, fp1, ft); +            gen_load_fpr32(ctx, fp2, fr); +            gen_helper_float_nmsub_s(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i32(fp0); +            tcg_temp_free_i32(fp1); +            gen_store_fpr32(ctx, fp2, fd); +            tcg_temp_free_i32(fp2); +        } +        opn = "nmsub.s"; +        break; +    case OPC_NMSUB_D: +        check_cop1x(ctx); +        check_cp1_registers(ctx, fd | fs | ft | fr); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            TCGv_i64 fp2 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_load_fpr64(ctx, fp2, fr); +            gen_helper_float_nmsub_d(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i64(fp0); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp2, fd); +            tcg_temp_free_i64(fp2); +        } +        opn = "nmsub.d"; +        break; +    case OPC_NMSUB_PS: +        check_ps(ctx); +        { +            TCGv_i64 fp0 = tcg_temp_new_i64(); +            TCGv_i64 fp1 = tcg_temp_new_i64(); +            TCGv_i64 fp2 = tcg_temp_new_i64(); + +            gen_load_fpr64(ctx, fp0, fs); +            gen_load_fpr64(ctx, fp1, ft); +            gen_load_fpr64(ctx, fp2, fr); +            gen_helper_float_nmsub_ps(fp2, cpu_env, fp0, fp1, fp2); +            tcg_temp_free_i64(fp0); +            tcg_temp_free_i64(fp1); +            gen_store_fpr64(ctx, fp2, fd); +            tcg_temp_free_i64(fp2); +        } +        opn = "nmsub.ps"; +        break; +    default: +        MIPS_INVAL(opn); +        generate_exception (ctx, EXCP_RI); +        return; +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s, %s, %s", opn, fregnames[fd], fregnames[fr], +               fregnames[fs], fregnames[ft]); +} + +static void gen_rdhwr(DisasContext *ctx, int rt, int rd) +{ +    TCGv t0; + +#if !defined(CONFIG_USER_ONLY) +    /* The Linux kernel will emulate rdhwr if it's not supported natively. +       Therefore only check the ISA in system mode.  */ +    check_insn(ctx, ISA_MIPS32R2); +#endif +    t0 = tcg_temp_new(); + +    switch (rd) { +    case 0: +        save_cpu_state(ctx, 1); +        gen_helper_rdhwr_cpunum(t0, cpu_env); +        gen_store_gpr(t0, rt); +        break; +    case 1: +        save_cpu_state(ctx, 1); +        gen_helper_rdhwr_synci_step(t0, cpu_env); +        gen_store_gpr(t0, rt); +        break; +    case 2: +        save_cpu_state(ctx, 1); +        gen_helper_rdhwr_cc(t0, cpu_env); +        gen_store_gpr(t0, rt); +        break; +    case 3: +        save_cpu_state(ctx, 1); +        gen_helper_rdhwr_ccres(t0, cpu_env); +        gen_store_gpr(t0, rt); +        break; +    case 29: +#if defined(CONFIG_USER_ONLY) +        tcg_gen_ld_tl(t0, cpu_env, +                      offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); +        gen_store_gpr(t0, rt); +        break; +#else +        if ((ctx->hflags & MIPS_HFLAG_CP0) || +            (ctx->hflags & MIPS_HFLAG_HWRENA_ULR)) { +            tcg_gen_ld_tl(t0, cpu_env, +                          offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); +            gen_store_gpr(t0, rt); +        } else { +            generate_exception(ctx, EXCP_RI); +        } +        break; +#endif +    default:            /* Invalid */ +        MIPS_INVAL("rdhwr"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +    tcg_temp_free(t0); +} + +static inline void clear_branch_hflags(DisasContext *ctx) +{ +    ctx->hflags &= ~MIPS_HFLAG_BMASK; +    if (ctx->bstate == BS_NONE) { +        save_cpu_state(ctx, 0); +    } else { +        /* it is not safe to save ctx->hflags as hflags may be changed +           in execution time by the instruction in delay / forbidden slot. */ +        tcg_gen_andi_i32(hflags, hflags, ~MIPS_HFLAG_BMASK); +    } +} + +static void gen_branch(DisasContext *ctx, int insn_bytes) +{ +    if (ctx->hflags & MIPS_HFLAG_BMASK) { +        int proc_hflags = ctx->hflags & MIPS_HFLAG_BMASK; +        /* Branches completion */ +        clear_branch_hflags(ctx); +        ctx->bstate = BS_BRANCH; +        /* FIXME: Need to clear can_do_io.  */ +        switch (proc_hflags & MIPS_HFLAG_BMASK_BASE) { +        case MIPS_HFLAG_FBNSLOT: +            MIPS_DEBUG("forbidden slot"); +            gen_goto_tb(ctx, 0, ctx->pc + insn_bytes); +            break; +        case MIPS_HFLAG_B: +            /* unconditional branch */ +            MIPS_DEBUG("unconditional branch"); +            if (proc_hflags & MIPS_HFLAG_BX) { +                tcg_gen_xori_i32(hflags, hflags, MIPS_HFLAG_M16); +            } +            gen_goto_tb(ctx, 0, ctx->btarget); +            break; +        case MIPS_HFLAG_BL: +            /* blikely taken case */ +            MIPS_DEBUG("blikely branch taken"); +            gen_goto_tb(ctx, 0, ctx->btarget); +            break; +        case MIPS_HFLAG_BC: +            /* Conditional branch */ +            MIPS_DEBUG("conditional branch"); +            { +                TCGLabel *l1 = gen_new_label(); + +                tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); +                gen_goto_tb(ctx, 1, ctx->pc + insn_bytes); +                gen_set_label(l1); +                gen_goto_tb(ctx, 0, ctx->btarget); +            } +            break; +        case MIPS_HFLAG_BR: +            /* unconditional branch to register */ +            MIPS_DEBUG("branch to register"); +            if (ctx->insn_flags & (ASE_MIPS16 | ASE_MICROMIPS)) { +                TCGv t0 = tcg_temp_new(); +                TCGv_i32 t1 = tcg_temp_new_i32(); + +                tcg_gen_andi_tl(t0, btarget, 0x1); +                tcg_gen_trunc_tl_i32(t1, t0); +                tcg_temp_free(t0); +                tcg_gen_andi_i32(hflags, hflags, ~(uint32_t)MIPS_HFLAG_M16); +                tcg_gen_shli_i32(t1, t1, MIPS_HFLAG_M16_SHIFT); +                tcg_gen_or_i32(hflags, hflags, t1); +                tcg_temp_free_i32(t1); + +                tcg_gen_andi_tl(cpu_PC, btarget, ~(target_ulong)0x1); +            } else { +                tcg_gen_mov_tl(cpu_PC, btarget); +            } +            if (ctx->singlestep_enabled) { +                save_cpu_state(ctx, 0); +                gen_helper_0e0i(raise_exception, EXCP_DEBUG); +            } +            tcg_gen_exit_tb(0); +            break; +        default: +            fprintf(stderr, "unknown branch 0x%x\n", proc_hflags); +            abort(); +        } +    } +} + +/* Compact Branches */ +static void gen_compute_compact_branch(DisasContext *ctx, uint32_t opc, +                                       int rs, int rt, int32_t offset) +{ +    int bcond_compute = 0; +    TCGv t0 = tcg_temp_new(); +    TCGv t1 = tcg_temp_new(); +    int m16_lowbit = (ctx->hflags & MIPS_HFLAG_M16) != 0; + +    if (ctx->hflags & MIPS_HFLAG_BMASK) { +#ifdef MIPS_DEBUG_DISAS +        LOG_DISAS("Branch in delay / forbidden slot at PC 0x" TARGET_FMT_lx +                  "\n", ctx->pc); +#endif +        generate_exception(ctx, EXCP_RI); +        goto out; +    } + +    /* Load needed operands and calculate btarget */ +    switch (opc) { +    /* compact branch */ +    case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */ +    case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */ +        gen_load_gpr(t0, rs); +        gen_load_gpr(t1, rt); +        bcond_compute = 1; +        ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); +        if (rs <= rt && rs == 0) { +            /* OPC_BEQZALC, OPC_BNEZALC */ +            tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); +        } +        break; +    case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ +    case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */ +        gen_load_gpr(t0, rs); +        gen_load_gpr(t1, rt); +        bcond_compute = 1; +        ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); +        break; +    case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ +    case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ +        if (rs == 0 || rs == rt) { +            /* OPC_BLEZALC, OPC_BGEZALC */ +            /* OPC_BGTZALC, OPC_BLTZALC */ +            tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); +        } +        gen_load_gpr(t0, rs); +        gen_load_gpr(t1, rt); +        bcond_compute = 1; +        ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); +        break; +    case OPC_BC: +    case OPC_BALC: +        ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); +        break; +    case OPC_BEQZC: +    case OPC_BNEZC: +        if (rs != 0) { +            /* OPC_BEQZC, OPC_BNEZC */ +            gen_load_gpr(t0, rs); +            bcond_compute = 1; +            ctx->btarget = addr_add(ctx, ctx->pc + 4, offset); +        } else { +            /* OPC_JIC, OPC_JIALC */ +            TCGv tbase = tcg_temp_new(); +            TCGv toffset = tcg_temp_new(); + +            gen_load_gpr(tbase, rt); +            tcg_gen_movi_tl(toffset, offset); +            gen_op_addr_add(ctx, btarget, tbase, toffset); +            tcg_temp_free(tbase); +            tcg_temp_free(toffset); +        } +        break; +    default: +        MIPS_INVAL("Compact branch/jump"); +        generate_exception(ctx, EXCP_RI); +        goto out; +    } + +    if (bcond_compute == 0) { +        /* Uncoditional compact branch */ +        switch (opc) { +        case OPC_JIALC: +            tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); +            /* Fallthrough */ +        case OPC_JIC: +            ctx->hflags |= MIPS_HFLAG_BR; +            break; +        case OPC_BALC: +            tcg_gen_movi_tl(cpu_gpr[31], ctx->pc + 4 + m16_lowbit); +            /* Fallthrough */ +        case OPC_BC: +            ctx->hflags |= MIPS_HFLAG_B; +            break; +        default: +            MIPS_INVAL("Compact branch/jump"); +            generate_exception(ctx, EXCP_RI); +            goto out; +        } + +        /* Generating branch here as compact branches don't have delay slot */ +        gen_branch(ctx, 4); +    } else { +        /* Conditional compact branch */ +        TCGLabel *fs = gen_new_label(); +        save_cpu_state(ctx, 0); + +        switch (opc) { +        case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC */ +            if (rs == 0 && rt != 0) { +                /* OPC_BLEZALC */ +                tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); +            } else if (rs != 0 && rt != 0 && rs == rt) { +                /* OPC_BGEZALC */ +                tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); +            } else { +                /* OPC_BGEUC */ +                tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GEU), t0, t1, fs); +            } +            break; +        case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC */ +            if (rs == 0 && rt != 0) { +                /* OPC_BGTZALC */ +                tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); +            } else if (rs != 0 && rt != 0 && rs == rt) { +                /* OPC_BLTZALC */ +                tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); +            } else { +                /* OPC_BLTUC */ +                tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LTU), t0, t1, fs); +            } +            break; +        case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC */ +            if (rs == 0 && rt != 0) { +                /* OPC_BLEZC */ +                tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); +            } else if (rs != 0 && rt != 0 && rs == rt) { +                /* OPC_BGEZC */ +                tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); +            } else { +                /* OPC_BGEC */ +                tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GE), t0, t1, fs); +            } +            break; +        case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC */ +            if (rs == 0 && rt != 0) { +                /* OPC_BGTZC */ +                tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); +            } else if (rs != 0 && rt != 0 && rs == rt) { +                /* OPC_BLTZC */ +                tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); +            } else { +                /* OPC_BLTC */ +                tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LT), t0, t1, fs); +            } +            break; +        case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC */ +        case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */ +            if (rs >= rt) { +                /* OPC_BOVC, OPC_BNVC */ +                TCGv t2 = tcg_temp_new(); +                TCGv t3 = tcg_temp_new(); +                TCGv t4 = tcg_temp_new(); +                TCGv input_overflow = tcg_temp_new(); + +                gen_load_gpr(t0, rs); +                gen_load_gpr(t1, rt); +                tcg_gen_ext32s_tl(t2, t0); +                tcg_gen_setcond_tl(TCG_COND_NE, input_overflow, t2, t0); +                tcg_gen_ext32s_tl(t3, t1); +                tcg_gen_setcond_tl(TCG_COND_NE, t4, t3, t1); +                tcg_gen_or_tl(input_overflow, input_overflow, t4); + +                tcg_gen_add_tl(t4, t2, t3); +                tcg_gen_ext32s_tl(t4, t4); +                tcg_gen_xor_tl(t2, t2, t3); +                tcg_gen_xor_tl(t3, t4, t3); +                tcg_gen_andc_tl(t2, t3, t2); +                tcg_gen_setcondi_tl(TCG_COND_LT, t4, t2, 0); +                tcg_gen_or_tl(t4, t4, input_overflow); +                if (opc == OPC_BOVC) { +                    /* OPC_BOVC */ +                    tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t4, 0, fs); +                } else { +                    /* OPC_BNVC */ +                    tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t4, 0, fs); +                } +                tcg_temp_free(input_overflow); +                tcg_temp_free(t4); +                tcg_temp_free(t3); +                tcg_temp_free(t2); +            } else if (rs < rt && rs == 0) { +                /* OPC_BEQZALC, OPC_BNEZALC */ +                if (opc == OPC_BEQZALC) { +                    /* OPC_BEQZALC */ +                    tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t1, 0, fs); +                } else { +                    /* OPC_BNEZALC */ +                    tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t1, 0, fs); +                } +            } else { +                /* OPC_BEQC, OPC_BNEC */ +                if (opc == OPC_BEQC) { +                    /* OPC_BEQC */ +                    tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_EQ), t0, t1, fs); +                } else { +                    /* OPC_BNEC */ +                    tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_NE), t0, t1, fs); +                } +            } +            break; +        case OPC_BEQZC: +            tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t0, 0, fs); +            break; +        case OPC_BNEZC: +            tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_NE), t0, 0, fs); +            break; +        default: +            MIPS_INVAL("Compact conditional branch/jump"); +            generate_exception(ctx, EXCP_RI); +            goto out; +        } + +        /* Generating branch here as compact branches don't have delay slot */ +        gen_goto_tb(ctx, 1, ctx->btarget); +        gen_set_label(fs); + +        ctx->hflags |= MIPS_HFLAG_FBNSLOT; +        MIPS_DEBUG("Compact conditional branch"); +    } + +out: +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +/* ISA extensions (ASEs) */ +/* MIPS16 extension to MIPS32 */ + +/* MIPS16 major opcodes */ +enum { +  M16_OPC_ADDIUSP = 0x00, +  M16_OPC_ADDIUPC = 0x01, +  M16_OPC_B = 0x02, +  M16_OPC_JAL = 0x03, +  M16_OPC_BEQZ = 0x04, +  M16_OPC_BNEQZ = 0x05, +  M16_OPC_SHIFT = 0x06, +  M16_OPC_LD = 0x07, +  M16_OPC_RRIA = 0x08, +  M16_OPC_ADDIU8 = 0x09, +  M16_OPC_SLTI = 0x0a, +  M16_OPC_SLTIU = 0x0b, +  M16_OPC_I8 = 0x0c, +  M16_OPC_LI = 0x0d, +  M16_OPC_CMPI = 0x0e, +  M16_OPC_SD = 0x0f, +  M16_OPC_LB = 0x10, +  M16_OPC_LH = 0x11, +  M16_OPC_LWSP = 0x12, +  M16_OPC_LW = 0x13, +  M16_OPC_LBU = 0x14, +  M16_OPC_LHU = 0x15, +  M16_OPC_LWPC = 0x16, +  M16_OPC_LWU = 0x17, +  M16_OPC_SB = 0x18, +  M16_OPC_SH = 0x19, +  M16_OPC_SWSP = 0x1a, +  M16_OPC_SW = 0x1b, +  M16_OPC_RRR = 0x1c, +  M16_OPC_RR = 0x1d, +  M16_OPC_EXTEND = 0x1e, +  M16_OPC_I64 = 0x1f +}; + +/* I8 funct field */ +enum { +  I8_BTEQZ = 0x0, +  I8_BTNEZ = 0x1, +  I8_SWRASP = 0x2, +  I8_ADJSP = 0x3, +  I8_SVRS = 0x4, +  I8_MOV32R = 0x5, +  I8_MOVR32 = 0x7 +}; + +/* RRR f field */ +enum { +  RRR_DADDU = 0x0, +  RRR_ADDU = 0x1, +  RRR_DSUBU = 0x2, +  RRR_SUBU = 0x3 +}; + +/* RR funct field */ +enum { +  RR_JR = 0x00, +  RR_SDBBP = 0x01, +  RR_SLT = 0x02, +  RR_SLTU = 0x03, +  RR_SLLV = 0x04, +  RR_BREAK = 0x05, +  RR_SRLV = 0x06, +  RR_SRAV = 0x07, +  RR_DSRL = 0x08, +  RR_CMP = 0x0a, +  RR_NEG = 0x0b, +  RR_AND = 0x0c, +  RR_OR = 0x0d, +  RR_XOR = 0x0e, +  RR_NOT = 0x0f, +  RR_MFHI = 0x10, +  RR_CNVT = 0x11, +  RR_MFLO = 0x12, +  RR_DSRA = 0x13, +  RR_DSLLV = 0x14, +  RR_DSRLV = 0x16, +  RR_DSRAV = 0x17, +  RR_MULT = 0x18, +  RR_MULTU = 0x19, +  RR_DIV = 0x1a, +  RR_DIVU = 0x1b, +  RR_DMULT = 0x1c, +  RR_DMULTU = 0x1d, +  RR_DDIV = 0x1e, +  RR_DDIVU = 0x1f +}; + +/* I64 funct field */ +enum { +  I64_LDSP = 0x0, +  I64_SDSP = 0x1, +  I64_SDRASP = 0x2, +  I64_DADJSP = 0x3, +  I64_LDPC = 0x4, +  I64_DADDIU5 = 0x5, +  I64_DADDIUPC = 0x6, +  I64_DADDIUSP = 0x7 +}; + +/* RR ry field for CNVT */ +enum { +  RR_RY_CNVT_ZEB = 0x0, +  RR_RY_CNVT_ZEH = 0x1, +  RR_RY_CNVT_ZEW = 0x2, +  RR_RY_CNVT_SEB = 0x4, +  RR_RY_CNVT_SEH = 0x5, +  RR_RY_CNVT_SEW = 0x6, +}; + +static int xlat (int r) +{ +  static int map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; + +  return map[r]; +} + +static void gen_mips16_save (DisasContext *ctx, +                             int xsregs, int aregs, +                             int do_ra, int do_s0, int do_s1, +                             int framesize) +{ +    TCGv t0 = tcg_temp_new(); +    TCGv t1 = tcg_temp_new(); +    TCGv t2 = tcg_temp_new(); +    int args, astatic; + +    switch (aregs) { +    case 0: +    case 1: +    case 2: +    case 3: +    case 11: +        args = 0; +        break; +    case 4: +    case 5: +    case 6: +    case 7: +        args = 1; +        break; +    case 8: +    case 9: +    case 10: +        args = 2; +        break; +    case 12: +    case 13: +        args = 3; +        break; +    case 14: +        args = 4; +        break; +    default: +        generate_exception(ctx, EXCP_RI); +        return; +    } + +    switch (args) { +    case 4: +        gen_base_offset_addr(ctx, t0, 29, 12); +        gen_load_gpr(t1, 7); +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); +        /* Fall through */ +    case 3: +        gen_base_offset_addr(ctx, t0, 29, 8); +        gen_load_gpr(t1, 6); +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); +        /* Fall through */ +    case 2: +        gen_base_offset_addr(ctx, t0, 29, 4); +        gen_load_gpr(t1, 5); +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); +        /* Fall through */ +    case 1: +        gen_base_offset_addr(ctx, t0, 29, 0); +        gen_load_gpr(t1, 4); +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); +    } + +    gen_load_gpr(t0, 29); + +#define DECR_AND_STORE(reg) do {                                 \ +        tcg_gen_movi_tl(t2, -4);                                 \ +        gen_op_addr_add(ctx, t0, t0, t2);                        \ +        gen_load_gpr(t1, reg);                                   \ +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); \ +    } while (0) + +    if (do_ra) { +        DECR_AND_STORE(31); +    } + +    switch (xsregs) { +    case 7: +        DECR_AND_STORE(30); +        /* Fall through */ +    case 6: +        DECR_AND_STORE(23); +        /* Fall through */ +    case 5: +        DECR_AND_STORE(22); +        /* Fall through */ +    case 4: +        DECR_AND_STORE(21); +        /* Fall through */ +    case 3: +        DECR_AND_STORE(20); +        /* Fall through */ +    case 2: +        DECR_AND_STORE(19); +        /* Fall through */ +    case 1: +        DECR_AND_STORE(18); +    } + +    if (do_s1) { +        DECR_AND_STORE(17); +    } +    if (do_s0) { +        DECR_AND_STORE(16); +    } + +    switch (aregs) { +    case 0: +    case 4: +    case 8: +    case 12: +    case 14: +        astatic = 0; +        break; +    case 1: +    case 5: +    case 9: +    case 13: +        astatic = 1; +        break; +    case 2: +    case 6: +    case 10: +        astatic = 2; +        break; +    case 3: +    case 7: +        astatic = 3; +        break; +    case 11: +        astatic = 4; +        break; +    default: +        generate_exception(ctx, EXCP_RI); +        return; +    } + +    if (astatic > 0) { +        DECR_AND_STORE(7); +        if (astatic > 1) { +            DECR_AND_STORE(6); +            if (astatic > 2) { +                DECR_AND_STORE(5); +                if (astatic > 3) { +                    DECR_AND_STORE(4); +                } +            } +        } +    } +#undef DECR_AND_STORE + +    tcg_gen_movi_tl(t2, -framesize); +    gen_op_addr_add(ctx, cpu_gpr[29], cpu_gpr[29], t2); +    tcg_temp_free(t0); +    tcg_temp_free(t1); +    tcg_temp_free(t2); +} + +static void gen_mips16_restore (DisasContext *ctx, +                                int xsregs, int aregs, +                                int do_ra, int do_s0, int do_s1, +                                int framesize) +{ +    int astatic; +    TCGv t0 = tcg_temp_new(); +    TCGv t1 = tcg_temp_new(); +    TCGv t2 = tcg_temp_new(); + +    tcg_gen_movi_tl(t2, framesize); +    gen_op_addr_add(ctx, t0, cpu_gpr[29], t2); + +#define DECR_AND_LOAD(reg) do {                            \ +        tcg_gen_movi_tl(t2, -4);                           \ +        gen_op_addr_add(ctx, t0, t0, t2);                  \ +        tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); \ +        gen_store_gpr(t1, reg);                            \ +    } while (0) + +    if (do_ra) { +        DECR_AND_LOAD(31); +    } + +    switch (xsregs) { +    case 7: +        DECR_AND_LOAD(30); +        /* Fall through */ +    case 6: +        DECR_AND_LOAD(23); +        /* Fall through */ +    case 5: +        DECR_AND_LOAD(22); +        /* Fall through */ +    case 4: +        DECR_AND_LOAD(21); +        /* Fall through */ +    case 3: +        DECR_AND_LOAD(20); +        /* Fall through */ +    case 2: +        DECR_AND_LOAD(19); +        /* Fall through */ +    case 1: +        DECR_AND_LOAD(18); +    } + +    if (do_s1) { +        DECR_AND_LOAD(17); +    } +    if (do_s0) { +        DECR_AND_LOAD(16); +    } + +    switch (aregs) { +    case 0: +    case 4: +    case 8: +    case 12: +    case 14: +        astatic = 0; +        break; +    case 1: +    case 5: +    case 9: +    case 13: +        astatic = 1; +        break; +    case 2: +    case 6: +    case 10: +        astatic = 2; +        break; +    case 3: +    case 7: +        astatic = 3; +        break; +    case 11: +        astatic = 4; +        break; +    default: +        generate_exception(ctx, EXCP_RI); +        return; +    } + +    if (astatic > 0) { +        DECR_AND_LOAD(7); +        if (astatic > 1) { +            DECR_AND_LOAD(6); +            if (astatic > 2) { +                DECR_AND_LOAD(5); +                if (astatic > 3) { +                    DECR_AND_LOAD(4); +                } +            } +        } +    } +#undef DECR_AND_LOAD + +    tcg_gen_movi_tl(t2, framesize); +    gen_op_addr_add(ctx, cpu_gpr[29], cpu_gpr[29], t2); +    tcg_temp_free(t0); +    tcg_temp_free(t1); +    tcg_temp_free(t2); +} + +static void gen_addiupc (DisasContext *ctx, int rx, int imm, +                         int is_64_bit, int extended) +{ +    TCGv t0; + +    if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { +        generate_exception(ctx, EXCP_RI); +        return; +    } + +    t0 = tcg_temp_new(); + +    tcg_gen_movi_tl(t0, pc_relative_pc(ctx)); +    tcg_gen_addi_tl(cpu_gpr[rx], t0, imm); +    if (!is_64_bit) { +        tcg_gen_ext32s_tl(cpu_gpr[rx], cpu_gpr[rx]); +    } + +    tcg_temp_free(t0); +} + +#if defined(TARGET_MIPS64) +static void decode_i64_mips16 (DisasContext *ctx, +                               int ry, int funct, int16_t offset, +                               int extended) +{ +    switch (funct) { +    case I64_LDSP: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        offset = extended ? offset : offset << 3; +        gen_ld(ctx, OPC_LD, ry, 29, offset); +        break; +    case I64_SDSP: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        offset = extended ? offset : offset << 3; +        gen_st(ctx, OPC_SD, ry, 29, offset); +        break; +    case I64_SDRASP: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        offset = extended ? offset : (ctx->opcode & 0xff) << 3; +        gen_st(ctx, OPC_SD, 31, 29, offset); +        break; +    case I64_DADJSP: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        offset = extended ? offset : ((int8_t)ctx->opcode) << 3; +        gen_arith_imm(ctx, OPC_DADDIU, 29, 29, offset); +        break; +    case I64_LDPC: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        if (extended && (ctx->hflags & MIPS_HFLAG_BMASK)) { +            generate_exception(ctx, EXCP_RI); +        } else { +            offset = extended ? offset : offset << 3; +            gen_ld(ctx, OPC_LDPC, ry, 0, offset); +        } +        break; +    case I64_DADDIU5: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        offset = extended ? offset : ((int8_t)(offset << 3)) >> 3; +        gen_arith_imm(ctx, OPC_DADDIU, ry, ry, offset); +        break; +    case I64_DADDIUPC: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        offset = extended ? offset : offset << 2; +        gen_addiupc(ctx, ry, offset, 1, extended); +        break; +    case I64_DADDIUSP: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        offset = extended ? offset : offset << 2; +        gen_arith_imm(ctx, OPC_DADDIU, ry, 29, offset); +        break; +    } +} +#endif + +static int decode_extended_mips16_opc (CPUMIPSState *env, DisasContext *ctx) +{ +    int extend = cpu_lduw_code(env, ctx->pc + 2); +    int op, rx, ry, funct, sa; +    int16_t imm, offset; + +    ctx->opcode = (ctx->opcode << 16) | extend; +    op = (ctx->opcode >> 11) & 0x1f; +    sa = (ctx->opcode >> 22) & 0x1f; +    funct = (ctx->opcode >> 8) & 0x7; +    rx = xlat((ctx->opcode >> 8) & 0x7); +    ry = xlat((ctx->opcode >> 5) & 0x7); +    offset = imm = (int16_t) (((ctx->opcode >> 16) & 0x1f) << 11 +                              | ((ctx->opcode >> 21) & 0x3f) << 5 +                              | (ctx->opcode & 0x1f)); + +    /* The extended opcodes cleverly reuse the opcodes from their 16-bit +       counterparts.  */ +    switch (op) { +    case M16_OPC_ADDIUSP: +        gen_arith_imm(ctx, OPC_ADDIU, rx, 29, imm); +        break; +    case M16_OPC_ADDIUPC: +        gen_addiupc(ctx, rx, imm, 0, 1); +        break; +    case M16_OPC_B: +        gen_compute_branch(ctx, OPC_BEQ, 4, 0, 0, offset << 1, 0); +        /* No delay slot, so just process as a normal instruction */ +        break; +    case M16_OPC_BEQZ: +        gen_compute_branch(ctx, OPC_BEQ, 4, rx, 0, offset << 1, 0); +        /* No delay slot, so just process as a normal instruction */ +        break; +    case M16_OPC_BNEQZ: +        gen_compute_branch(ctx, OPC_BNE, 4, rx, 0, offset << 1, 0); +        /* No delay slot, so just process as a normal instruction */ +        break; +    case M16_OPC_SHIFT: +        switch (ctx->opcode & 0x3) { +        case 0x0: +            gen_shift_imm(ctx, OPC_SLL, rx, ry, sa); +            break; +        case 0x1: +#if defined(TARGET_MIPS64) +            check_mips_64(ctx); +            gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); +#else +            generate_exception(ctx, EXCP_RI); +#endif +            break; +        case 0x2: +            gen_shift_imm(ctx, OPC_SRL, rx, ry, sa); +            break; +        case 0x3: +            gen_shift_imm(ctx, OPC_SRA, rx, ry, sa); +            break; +        } +        break; +#if defined(TARGET_MIPS64) +    case M16_OPC_LD: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_ld(ctx, OPC_LD, ry, rx, offset); +        break; +#endif +    case M16_OPC_RRIA: +        imm = ctx->opcode & 0xf; +        imm = imm | ((ctx->opcode >> 20) & 0x7f) << 4; +        imm = imm | ((ctx->opcode >> 16) & 0xf) << 11; +        imm = (int16_t) (imm << 1) >> 1; +        if ((ctx->opcode >> 4) & 0x1) { +#if defined(TARGET_MIPS64) +            check_mips_64(ctx); +            gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); +#else +            generate_exception(ctx, EXCP_RI); +#endif +        } else { +            gen_arith_imm(ctx, OPC_ADDIU, ry, rx, imm); +        } +        break; +    case M16_OPC_ADDIU8: +        gen_arith_imm(ctx, OPC_ADDIU, rx, rx, imm); +        break; +    case M16_OPC_SLTI: +        gen_slt_imm(ctx, OPC_SLTI, 24, rx, imm); +        break; +    case M16_OPC_SLTIU: +        gen_slt_imm(ctx, OPC_SLTIU, 24, rx, imm); +        break; +    case M16_OPC_I8: +        switch (funct) { +        case I8_BTEQZ: +            gen_compute_branch(ctx, OPC_BEQ, 4, 24, 0, offset << 1, 0); +            break; +        case I8_BTNEZ: +            gen_compute_branch(ctx, OPC_BNE, 4, 24, 0, offset << 1, 0); +            break; +        case I8_SWRASP: +            gen_st(ctx, OPC_SW, 31, 29, imm); +            break; +        case I8_ADJSP: +            gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm); +            break; +        case I8_SVRS: +            check_insn(ctx, ISA_MIPS32); +            { +                int xsregs = (ctx->opcode >> 24) & 0x7; +                int aregs = (ctx->opcode >> 16) & 0xf; +                int do_ra = (ctx->opcode >> 6) & 0x1; +                int do_s0 = (ctx->opcode >> 5) & 0x1; +                int do_s1 = (ctx->opcode >> 4) & 0x1; +                int framesize = (((ctx->opcode >> 20) & 0xf) << 4 +                                 | (ctx->opcode & 0xf)) << 3; + +                if (ctx->opcode & (1 << 7)) { +                    gen_mips16_save(ctx, xsregs, aregs, +                                    do_ra, do_s0, do_s1, +                                    framesize); +                } else { +                    gen_mips16_restore(ctx, xsregs, aregs, +                                       do_ra, do_s0, do_s1, +                                       framesize); +                } +            } +            break; +        default: +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case M16_OPC_LI: +        tcg_gen_movi_tl(cpu_gpr[rx], (uint16_t) imm); +        break; +    case M16_OPC_CMPI: +        tcg_gen_xori_tl(cpu_gpr[24], cpu_gpr[rx], (uint16_t) imm); +        break; +#if defined(TARGET_MIPS64) +    case M16_OPC_SD: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_st(ctx, OPC_SD, ry, rx, offset); +        break; +#endif +    case M16_OPC_LB: +        gen_ld(ctx, OPC_LB, ry, rx, offset); +        break; +    case M16_OPC_LH: +        gen_ld(ctx, OPC_LH, ry, rx, offset); +        break; +    case M16_OPC_LWSP: +        gen_ld(ctx, OPC_LW, rx, 29, offset); +        break; +    case M16_OPC_LW: +        gen_ld(ctx, OPC_LW, ry, rx, offset); +        break; +    case M16_OPC_LBU: +        gen_ld(ctx, OPC_LBU, ry, rx, offset); +        break; +    case M16_OPC_LHU: +        gen_ld(ctx, OPC_LHU, ry, rx, offset); +        break; +    case M16_OPC_LWPC: +        gen_ld(ctx, OPC_LWPC, rx, 0, offset); +        break; +#if defined(TARGET_MIPS64) +    case M16_OPC_LWU: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_ld(ctx, OPC_LWU, ry, rx, offset); +        break; +#endif +    case M16_OPC_SB: +        gen_st(ctx, OPC_SB, ry, rx, offset); +        break; +    case M16_OPC_SH: +        gen_st(ctx, OPC_SH, ry, rx, offset); +        break; +    case M16_OPC_SWSP: +        gen_st(ctx, OPC_SW, rx, 29, offset); +        break; +    case M16_OPC_SW: +        gen_st(ctx, OPC_SW, ry, rx, offset); +        break; +#if defined(TARGET_MIPS64) +    case M16_OPC_I64: +        decode_i64_mips16(ctx, ry, funct, offset, 1); +        break; +#endif +    default: +        generate_exception(ctx, EXCP_RI); +        break; +    } + +    return 4; +} + +static inline bool is_uhi(int sdbbp_code) +{ +#ifdef CONFIG_USER_ONLY +    return false; +#else +    return semihosting_enabled() && sdbbp_code == 1; +#endif +} + +static int decode_mips16_opc (CPUMIPSState *env, DisasContext *ctx) +{ +    int rx, ry; +    int sa; +    int op, cnvt_op, op1, offset; +    int funct; +    int n_bytes; + +    op = (ctx->opcode >> 11) & 0x1f; +    sa = (ctx->opcode >> 2) & 0x7; +    sa = sa == 0 ? 8 : sa; +    rx = xlat((ctx->opcode >> 8) & 0x7); +    cnvt_op = (ctx->opcode >> 5) & 0x7; +    ry = xlat((ctx->opcode >> 5) & 0x7); +    op1 = offset = ctx->opcode & 0x1f; + +    n_bytes = 2; + +    switch (op) { +    case M16_OPC_ADDIUSP: +        { +            int16_t imm = ((uint8_t) ctx->opcode) << 2; + +            gen_arith_imm(ctx, OPC_ADDIU, rx, 29, imm); +        } +        break; +    case M16_OPC_ADDIUPC: +        gen_addiupc(ctx, rx, ((uint8_t) ctx->opcode) << 2, 0, 0); +        break; +    case M16_OPC_B: +        offset = (ctx->opcode & 0x7ff) << 1; +        offset = (int16_t)(offset << 4) >> 4; +        gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, offset, 0); +        /* No delay slot, so just process as a normal instruction */ +        break; +    case M16_OPC_JAL: +        offset = cpu_lduw_code(env, ctx->pc + 2); +        offset = (((ctx->opcode & 0x1f) << 21) +                  | ((ctx->opcode >> 5) & 0x1f) << 16 +                  | offset) << 2; +        op = ((ctx->opcode >> 10) & 0x1) ? OPC_JALX : OPC_JAL; +        gen_compute_branch(ctx, op, 4, rx, ry, offset, 2); +        n_bytes = 4; +        break; +    case M16_OPC_BEQZ: +        gen_compute_branch(ctx, OPC_BEQ, 2, rx, 0, +                           ((int8_t)ctx->opcode) << 1, 0); +        /* No delay slot, so just process as a normal instruction */ +        break; +    case M16_OPC_BNEQZ: +        gen_compute_branch(ctx, OPC_BNE, 2, rx, 0, +                           ((int8_t)ctx->opcode) << 1, 0); +        /* No delay slot, so just process as a normal instruction */ +        break; +    case M16_OPC_SHIFT: +        switch (ctx->opcode & 0x3) { +        case 0x0: +            gen_shift_imm(ctx, OPC_SLL, rx, ry, sa); +            break; +        case 0x1: +#if defined(TARGET_MIPS64) +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_shift_imm(ctx, OPC_DSLL, rx, ry, sa); +#else +            generate_exception(ctx, EXCP_RI); +#endif +            break; +        case 0x2: +            gen_shift_imm(ctx, OPC_SRL, rx, ry, sa); +            break; +        case 0x3: +            gen_shift_imm(ctx, OPC_SRA, rx, ry, sa); +            break; +        } +        break; +#if defined(TARGET_MIPS64) +    case M16_OPC_LD: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_ld(ctx, OPC_LD, ry, rx, offset << 3); +        break; +#endif +    case M16_OPC_RRIA: +        { +            int16_t imm = (int8_t)((ctx->opcode & 0xf) << 4) >> 4; + +            if ((ctx->opcode >> 4) & 1) { +#if defined(TARGET_MIPS64) +                check_insn(ctx, ISA_MIPS3); +                check_mips_64(ctx); +                gen_arith_imm(ctx, OPC_DADDIU, ry, rx, imm); +#else +                generate_exception(ctx, EXCP_RI); +#endif +            } else { +                gen_arith_imm(ctx, OPC_ADDIU, ry, rx, imm); +            } +        } +        break; +    case M16_OPC_ADDIU8: +        { +            int16_t imm = (int8_t) ctx->opcode; + +            gen_arith_imm(ctx, OPC_ADDIU, rx, rx, imm); +        } +        break; +    case M16_OPC_SLTI: +        { +            int16_t imm = (uint8_t) ctx->opcode; +            gen_slt_imm(ctx, OPC_SLTI, 24, rx, imm); +        } +        break; +    case M16_OPC_SLTIU: +        { +            int16_t imm = (uint8_t) ctx->opcode; +            gen_slt_imm(ctx, OPC_SLTIU, 24, rx, imm); +        } +        break; +    case M16_OPC_I8: +        { +            int reg32; + +            funct = (ctx->opcode >> 8) & 0x7; +            switch (funct) { +            case I8_BTEQZ: +                gen_compute_branch(ctx, OPC_BEQ, 2, 24, 0, +                                   ((int8_t)ctx->opcode) << 1, 0); +                break; +            case I8_BTNEZ: +                gen_compute_branch(ctx, OPC_BNE, 2, 24, 0, +                                   ((int8_t)ctx->opcode) << 1, 0); +                break; +            case I8_SWRASP: +                gen_st(ctx, OPC_SW, 31, 29, (ctx->opcode & 0xff) << 2); +                break; +            case I8_ADJSP: +                gen_arith_imm(ctx, OPC_ADDIU, 29, 29, +                              ((int8_t)ctx->opcode) << 3); +                break; +            case I8_SVRS: +                check_insn(ctx, ISA_MIPS32); +                { +                    int do_ra = ctx->opcode & (1 << 6); +                    int do_s0 = ctx->opcode & (1 << 5); +                    int do_s1 = ctx->opcode & (1 << 4); +                    int framesize = ctx->opcode & 0xf; + +                    if (framesize == 0) { +                        framesize = 128; +                    } else { +                        framesize = framesize << 3; +                    } + +                    if (ctx->opcode & (1 << 7)) { +                        gen_mips16_save(ctx, 0, 0, +                                        do_ra, do_s0, do_s1, framesize); +                    } else { +                        gen_mips16_restore(ctx, 0, 0, +                                           do_ra, do_s0, do_s1, framesize); +                    } +                } +                break; +            case I8_MOV32R: +                { +                    int rz = xlat(ctx->opcode & 0x7); + +                    reg32 = (((ctx->opcode >> 3) & 0x3) << 3) | +                        ((ctx->opcode >> 5) & 0x7); +                    gen_arith(ctx, OPC_ADDU, reg32, rz, 0); +                } +                break; +            case I8_MOVR32: +                reg32 = ctx->opcode & 0x1f; +                gen_arith(ctx, OPC_ADDU, ry, reg32, 0); +                break; +            default: +                generate_exception(ctx, EXCP_RI); +                break; +            } +        } +        break; +    case M16_OPC_LI: +        { +            int16_t imm = (uint8_t) ctx->opcode; + +            gen_arith_imm(ctx, OPC_ADDIU, rx, 0, imm); +        } +        break; +    case M16_OPC_CMPI: +        { +            int16_t imm = (uint8_t) ctx->opcode; +            gen_logic_imm(ctx, OPC_XORI, 24, rx, imm); +        } +        break; +#if defined(TARGET_MIPS64) +    case M16_OPC_SD: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_st(ctx, OPC_SD, ry, rx, offset << 3); +        break; +#endif +    case M16_OPC_LB: +        gen_ld(ctx, OPC_LB, ry, rx, offset); +        break; +    case M16_OPC_LH: +        gen_ld(ctx, OPC_LH, ry, rx, offset << 1); +        break; +    case M16_OPC_LWSP: +        gen_ld(ctx, OPC_LW, rx, 29, ((uint8_t)ctx->opcode) << 2); +        break; +    case M16_OPC_LW: +        gen_ld(ctx, OPC_LW, ry, rx, offset << 2); +        break; +    case M16_OPC_LBU: +        gen_ld(ctx, OPC_LBU, ry, rx, offset); +        break; +    case M16_OPC_LHU: +        gen_ld(ctx, OPC_LHU, ry, rx, offset << 1); +        break; +    case M16_OPC_LWPC: +        gen_ld(ctx, OPC_LWPC, rx, 0, ((uint8_t)ctx->opcode) << 2); +        break; +#if defined (TARGET_MIPS64) +    case M16_OPC_LWU: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_ld(ctx, OPC_LWU, ry, rx, offset << 2); +        break; +#endif +    case M16_OPC_SB: +        gen_st(ctx, OPC_SB, ry, rx, offset); +        break; +    case M16_OPC_SH: +        gen_st(ctx, OPC_SH, ry, rx, offset << 1); +        break; +    case M16_OPC_SWSP: +        gen_st(ctx, OPC_SW, rx, 29, ((uint8_t)ctx->opcode) << 2); +        break; +    case M16_OPC_SW: +        gen_st(ctx, OPC_SW, ry, rx, offset << 2); +        break; +    case M16_OPC_RRR: +        { +            int rz = xlat((ctx->opcode >> 2) & 0x7); +            int mips32_op; + +            switch (ctx->opcode & 0x3) { +            case RRR_ADDU: +                mips32_op = OPC_ADDU; +                break; +            case RRR_SUBU: +                mips32_op = OPC_SUBU; +                break; +#if defined(TARGET_MIPS64) +            case RRR_DADDU: +                mips32_op = OPC_DADDU; +                check_insn(ctx, ISA_MIPS3); +                check_mips_64(ctx); +                break; +            case RRR_DSUBU: +                mips32_op = OPC_DSUBU; +                check_insn(ctx, ISA_MIPS3); +                check_mips_64(ctx); +                break; +#endif +            default: +                generate_exception(ctx, EXCP_RI); +                goto done; +            } + +            gen_arith(ctx, mips32_op, rz, rx, ry); +        done: +            ; +        } +        break; +    case M16_OPC_RR: +        switch (op1) { +        case RR_JR: +            { +                int nd = (ctx->opcode >> 7) & 0x1; +                int link = (ctx->opcode >> 6) & 0x1; +                int ra = (ctx->opcode >> 5) & 0x1; + +                if (nd) { +                    check_insn(ctx, ISA_MIPS32); +                } + +                if (link) { +                    op = OPC_JALR; +                } else { +                    op = OPC_JR; +                } + +                gen_compute_branch(ctx, op, 2, ra ? 31 : rx, 31, 0, +                                   (nd ? 0 : 2)); +            } +            break; +        case RR_SDBBP: +            if (is_uhi(extract32(ctx->opcode, 5, 6))) { +                gen_helper_do_semihosting(cpu_env); +            } else { +                /* XXX: not clear which exception should be raised +                 *      when in debug mode... +                 */ +                check_insn(ctx, ISA_MIPS32); +                generate_exception(ctx, EXCP_DBp); +            } +            break; +        case RR_SLT: +            gen_slt(ctx, OPC_SLT, 24, rx, ry); +            break; +        case RR_SLTU: +            gen_slt(ctx, OPC_SLTU, 24, rx, ry); +            break; +        case RR_BREAK: +            generate_exception(ctx, EXCP_BREAK); +            break; +        case RR_SLLV: +            gen_shift(ctx, OPC_SLLV, ry, rx, ry); +            break; +        case RR_SRLV: +            gen_shift(ctx, OPC_SRLV, ry, rx, ry); +            break; +        case RR_SRAV: +            gen_shift(ctx, OPC_SRAV, ry, rx, ry); +            break; +#if defined (TARGET_MIPS64) +        case RR_DSRL: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_shift_imm(ctx, OPC_DSRL, ry, ry, sa); +            break; +#endif +        case RR_CMP: +            gen_logic(ctx, OPC_XOR, 24, rx, ry); +            break; +        case RR_NEG: +            gen_arith(ctx, OPC_SUBU, rx, 0, ry); +            break; +        case RR_AND: +            gen_logic(ctx, OPC_AND, rx, rx, ry); +            break; +        case RR_OR: +            gen_logic(ctx, OPC_OR, rx, rx, ry); +            break; +        case RR_XOR: +            gen_logic(ctx, OPC_XOR, rx, rx, ry); +            break; +        case RR_NOT: +            gen_logic(ctx, OPC_NOR, rx, ry, 0); +            break; +        case RR_MFHI: +            gen_HILO(ctx, OPC_MFHI, 0, rx); +            break; +        case RR_CNVT: +            check_insn(ctx, ISA_MIPS32); +            switch (cnvt_op) { +            case RR_RY_CNVT_ZEB: +                tcg_gen_ext8u_tl(cpu_gpr[rx], cpu_gpr[rx]); +                break; +            case RR_RY_CNVT_ZEH: +                tcg_gen_ext16u_tl(cpu_gpr[rx], cpu_gpr[rx]); +                break; +            case RR_RY_CNVT_SEB: +                tcg_gen_ext8s_tl(cpu_gpr[rx], cpu_gpr[rx]); +                break; +            case RR_RY_CNVT_SEH: +                tcg_gen_ext16s_tl(cpu_gpr[rx], cpu_gpr[rx]); +                break; +#if defined (TARGET_MIPS64) +            case RR_RY_CNVT_ZEW: +                check_insn(ctx, ISA_MIPS64); +                check_mips_64(ctx); +                tcg_gen_ext32u_tl(cpu_gpr[rx], cpu_gpr[rx]); +                break; +            case RR_RY_CNVT_SEW: +                check_insn(ctx, ISA_MIPS64); +                check_mips_64(ctx); +                tcg_gen_ext32s_tl(cpu_gpr[rx], cpu_gpr[rx]); +                break; +#endif +            default: +                generate_exception(ctx, EXCP_RI); +                break; +            } +            break; +        case RR_MFLO: +            gen_HILO(ctx, OPC_MFLO, 0, rx); +            break; +#if defined (TARGET_MIPS64) +        case RR_DSRA: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_shift_imm(ctx, OPC_DSRA, ry, ry, sa); +            break; +        case RR_DSLLV: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_shift(ctx, OPC_DSLLV, ry, rx, ry); +            break; +        case RR_DSRLV: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_shift(ctx, OPC_DSRLV, ry, rx, ry); +            break; +        case RR_DSRAV: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_shift(ctx, OPC_DSRAV, ry, rx, ry); +            break; +#endif +        case RR_MULT: +            gen_muldiv(ctx, OPC_MULT, 0, rx, ry); +            break; +        case RR_MULTU: +            gen_muldiv(ctx, OPC_MULTU, 0, rx, ry); +            break; +        case RR_DIV: +            gen_muldiv(ctx, OPC_DIV, 0, rx, ry); +            break; +        case RR_DIVU: +            gen_muldiv(ctx, OPC_DIVU, 0, rx, ry); +            break; +#if defined (TARGET_MIPS64) +        case RR_DMULT: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_muldiv(ctx, OPC_DMULT, 0, rx, ry); +            break; +        case RR_DMULTU: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_muldiv(ctx, OPC_DMULTU, 0, rx, ry); +            break; +        case RR_DDIV: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_muldiv(ctx, OPC_DDIV, 0, rx, ry); +            break; +        case RR_DDIVU: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_muldiv(ctx, OPC_DDIVU, 0, rx, ry); +            break; +#endif +        default: +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case M16_OPC_EXTEND: +        decode_extended_mips16_opc(env, ctx); +        n_bytes = 4; +        break; +#if defined(TARGET_MIPS64) +    case M16_OPC_I64: +        funct = (ctx->opcode >> 8) & 0x7; +        decode_i64_mips16(ctx, ry, funct, offset, 0); +        break; +#endif +    default: +        generate_exception(ctx, EXCP_RI); +        break; +    } + +    return n_bytes; +} + +/* microMIPS extension to MIPS32/MIPS64 */ + +/* + * microMIPS32/microMIPS64 major opcodes + * + * 1. MIPS Architecture for Programmers Volume II-B: + *      The microMIPS32 Instruction Set (Revision 3.05) + * + *    Table 6.2 microMIPS32 Encoding of Major Opcode Field + * + * 2. MIPS Architecture For Programmers Volume II-A: + *      The MIPS64 Instruction Set (Revision 3.51) + */ + +enum { +    POOL32A = 0x00, +    POOL16A = 0x01, +    LBU16 = 0x02, +    MOVE16 = 0x03, +    ADDI32 = 0x04, +    R6_LUI = 0x04, +    AUI = 0x04, +    LBU32 = 0x05, +    SB32 = 0x06, +    LB32 = 0x07, + +    POOL32B = 0x08, +    POOL16B = 0x09, +    LHU16 = 0x0a, +    ANDI16 = 0x0b, +    ADDIU32 = 0x0c, +    LHU32 = 0x0d, +    SH32 = 0x0e, +    LH32 = 0x0f, + +    POOL32I = 0x10, +    POOL16C = 0x11, +    LWSP16 = 0x12, +    POOL16D = 0x13, +    ORI32 = 0x14, +    POOL32F = 0x15, +    POOL32S = 0x16,  /* MIPS64 */ +    DADDIU32 = 0x17, /* MIPS64 */ + +    POOL32C = 0x18, +    LWGP16 = 0x19, +    LW16 = 0x1a, +    POOL16E = 0x1b, +    XORI32 = 0x1c, +    JALS32 = 0x1d, +    BOVC = 0x1d, +    BEQC = 0x1d, +    BEQZALC = 0x1d, +    ADDIUPC = 0x1e, +    PCREL = 0x1e, +    BNVC = 0x1f, +    BNEC = 0x1f, +    BNEZALC = 0x1f, + +    R6_BEQZC = 0x20, +    JIC = 0x20, +    POOL16F = 0x21, +    SB16 = 0x22, +    BEQZ16 = 0x23, +    BEQZC16 = 0x23, +    SLTI32 = 0x24, +    BEQ32 = 0x25, +    BC = 0x25, +    SWC132 = 0x26, +    LWC132 = 0x27, + +    /* 0x29 is reserved */ +    RES_29 = 0x29, +    R6_BNEZC = 0x28, +    JIALC = 0x28, +    SH16 = 0x2a, +    BNEZ16 = 0x2b, +    BNEZC16 = 0x2b, +    SLTIU32 = 0x2c, +    BNE32 = 0x2d, +    BALC = 0x2d, +    SDC132 = 0x2e, +    LDC132 = 0x2f, + +    /* 0x31 is reserved */ +    RES_31 = 0x31, +    BLEZALC = 0x30, +    BGEZALC = 0x30, +    BGEUC = 0x30, +    SWSP16 = 0x32, +    B16 = 0x33, +    BC16 = 0x33, +    ANDI32 = 0x34, +    J32 = 0x35, +    BGTZC = 0x35, +    BLTZC = 0x35, +    BLTC = 0x35, +    SD32 = 0x36, /* MIPS64 */ +    LD32 = 0x37, /* MIPS64 */ + +    /* 0x39 is reserved */ +    RES_39 = 0x39, +    BGTZALC = 0x38, +    BLTZALC = 0x38, +    BLTUC = 0x38, +    SW16 = 0x3a, +    LI16 = 0x3b, +    JALX32 = 0x3c, +    JAL32 = 0x3d, +    BLEZC = 0x3d, +    BGEZC = 0x3d, +    BGEC = 0x3d, +    SW32 = 0x3e, +    LW32 = 0x3f +}; + +/* PCREL Instructions perform PC-Relative address calculation. bits 20..16 */ +enum { +    ADDIUPC_00 = 0x00, +    ADDIUPC_07 = 0x07, +    AUIPC = 0x1e, +    ALUIPC = 0x1f, +    LWPC_08 = 0x08, +    LWPC_0F = 0x0F, +}; + +/* POOL32A encoding of minor opcode field */ + +enum { +    /* These opcodes are distinguished only by bits 9..6; those bits are +     * what are recorded below. */ +    SLL32 = 0x0, +    SRL32 = 0x1, +    SRA = 0x2, +    ROTR = 0x3, +    SELEQZ = 0x5, +    SELNEZ = 0x6, + +    SLLV = 0x0, +    SRLV = 0x1, +    SRAV = 0x2, +    ROTRV = 0x3, +    ADD = 0x4, +    ADDU32 = 0x5, +    SUB = 0x6, +    SUBU32 = 0x7, +    MUL = 0x8, +    AND = 0x9, +    OR32 = 0xa, +    NOR = 0xb, +    XOR32 = 0xc, +    SLT = 0xd, +    SLTU = 0xe, + +    MOVN = 0x0, +    R6_MUL  = 0x0, +    MOVZ = 0x1, +    MUH  = 0x1, +    MULU = 0x2, +    MUHU = 0x3, +    LWXS = 0x4, +    R6_DIV  = 0x4, +    MOD  = 0x5, +    R6_DIVU = 0x6, +    MODU = 0x7, + +    /* The following can be distinguished by their lower 6 bits. */ +    INS = 0x0c, +    LSA = 0x0f, +    ALIGN = 0x1f, +    EXT = 0x2c, +    POOL32AXF = 0x3c +}; + +/* POOL32AXF encoding of minor opcode field extension */ + +/* + * 1. MIPS Architecture for Programmers Volume II-B: + *      The microMIPS32 Instruction Set (Revision 3.05) + * + *    Table 6.5 POOL32Axf Encoding of Minor Opcode Extension Field + * + * 2. MIPS Architecture for Programmers VolumeIV-e: + *      The MIPS DSP Application-Specific Extension + *        to the microMIPS32 Architecture (Revision 2.34) + * + *    Table 5.5 POOL32Axf Encoding of Minor Opcode Extension Field + */ + +enum { +    /* bits 11..6 */ +    TEQ = 0x00, +    TGE = 0x08, +    TGEU = 0x10, +    TLT = 0x20, +    TLTU = 0x28, +    TNE = 0x30, + +    MFC0 = 0x03, +    MTC0 = 0x0b, + +    /* begin of microMIPS32 DSP */ + +    /* bits 13..12 for 0x01 */ +    MFHI_ACC = 0x0, +    MFLO_ACC = 0x1, +    MTHI_ACC = 0x2, +    MTLO_ACC = 0x3, + +    /* bits 13..12 for 0x2a */ +    MADD_ACC = 0x0, +    MADDU_ACC = 0x1, +    MSUB_ACC = 0x2, +    MSUBU_ACC = 0x3, + +    /* bits 13..12 for 0x32 */ +    MULT_ACC = 0x0, +    MULTU_ACC = 0x1, + +    /* end of microMIPS32 DSP */ + +    /* bits 15..12 for 0x2c */ +    BITSWAP = 0x0, +    SEB = 0x2, +    SEH = 0x3, +    CLO = 0x4, +    CLZ = 0x5, +    RDHWR = 0x6, +    WSBH = 0x7, +    MULT = 0x8, +    MULTU = 0x9, +    DIV = 0xa, +    DIVU = 0xb, +    MADD = 0xc, +    MADDU = 0xd, +    MSUB = 0xe, +    MSUBU = 0xf, + +    /* bits 15..12 for 0x34 */ +    MFC2 = 0x4, +    MTC2 = 0x5, +    MFHC2 = 0x8, +    MTHC2 = 0x9, +    CFC2 = 0xc, +    CTC2 = 0xd, + +    /* bits 15..12 for 0x3c */ +    JALR = 0x0, +    JR = 0x0,                   /* alias */ +    JALRC = 0x0, +    JRC = 0x0, +    JALR_HB = 0x1, +    JALRC_HB = 0x1, +    JALRS = 0x4, +    JALRS_HB = 0x5, + +    /* bits 15..12 for 0x05 */ +    RDPGPR = 0xe, +    WRPGPR = 0xf, + +    /* bits 15..12 for 0x0d */ +    TLBP = 0x0, +    TLBR = 0x1, +    TLBWI = 0x2, +    TLBWR = 0x3, +    TLBINV = 0x4, +    TLBINVF = 0x5, +    WAIT = 0x9, +    IRET = 0xd, +    DERET = 0xe, +    ERET = 0xf, + +    /* bits 15..12 for 0x15 */ +    DMT = 0x0, +    DVPE = 0x1, +    EMT = 0x2, +    EVPE = 0x3, + +    /* bits 15..12 for 0x1d */ +    DI = 0x4, +    EI = 0x5, + +    /* bits 15..12 for 0x2d */ +    SYNC = 0x6, +    SYSCALL = 0x8, +    SDBBP = 0xd, + +    /* bits 15..12 for 0x35 */ +    MFHI32 = 0x0, +    MFLO32 = 0x1, +    MTHI32 = 0x2, +    MTLO32 = 0x3, +}; + +/* POOL32B encoding of minor opcode field (bits 15..12) */ + +enum { +    LWC2 = 0x0, +    LWP = 0x1, +    LDP = 0x4, +    LWM32 = 0x5, +    CACHE = 0x6, +    LDM = 0x7, +    SWC2 = 0x8, +    SWP = 0x9, +    SDP = 0xc, +    SWM32 = 0xd, +    SDM = 0xf +}; + +/* POOL32C encoding of minor opcode field (bits 15..12) */ + +enum { +    LWL = 0x0, +    SWL = 0x8, +    LWR = 0x1, +    SWR = 0x9, +    PREF = 0x2, +    /* 0xa is reserved */ +    LL = 0x3, +    SC = 0xb, +    LDL = 0x4, +    SDL = 0xc, +    LDR = 0x5, +    SDR = 0xd, +    /* 0x6 is reserved */ +    LWU = 0xe, +    LLD = 0x7, +    SCD = 0xf +}; + +/* POOL32F encoding of minor opcode field (bits 5..0) */ + +enum { +    /* These are the bit 7..6 values */ +    ADD_FMT = 0x0, + +    SUB_FMT = 0x1, + +    MUL_FMT = 0x2, + +    DIV_FMT = 0x3, + +    /* These are the bit 8..6 values */ +    MOVN_FMT = 0x0, +    RSQRT2_FMT = 0x0, +    MOVF_FMT = 0x0, +    RINT_FMT = 0x0, +    SELNEZ_FMT = 0x0, + +    MOVZ_FMT = 0x1, +    LWXC1 = 0x1, +    MOVT_FMT = 0x1, +    CLASS_FMT = 0x1, +    SELEQZ_FMT = 0x1, + +    PLL_PS = 0x2, +    SWXC1 = 0x2, +    SEL_FMT = 0x2, + +    PLU_PS = 0x3, +    LDXC1 = 0x3, + +    MOVN_FMT_04 = 0x4, +    PUL_PS = 0x4, +    SDXC1 = 0x4, +    RECIP2_FMT = 0x4, + +    MOVZ_FMT_05 = 0x05, +    PUU_PS = 0x5, +    LUXC1 = 0x5, + +    CVT_PS_S = 0x6, +    SUXC1 = 0x6, +    ADDR_PS = 0x6, +    PREFX = 0x6, +    MADDF_FMT = 0x6, + +    MULR_PS = 0x7, +    MSUBF_FMT = 0x7, + +    MADD_S = 0x01, +    MADD_D = 0x09, +    MADD_PS = 0x11, +    ALNV_PS = 0x19, +    MSUB_S = 0x21, +    MSUB_D = 0x29, +    MSUB_PS = 0x31, + +    NMADD_S = 0x02, +    NMADD_D = 0x0a, +    NMADD_PS = 0x12, +    NMSUB_S = 0x22, +    NMSUB_D = 0x2a, +    NMSUB_PS = 0x32, + +    MIN_FMT = 0x3, +    MAX_FMT = 0xb, +    MINA_FMT = 0x23, +    MAXA_FMT = 0x2b, +    POOL32FXF = 0x3b, + +    CABS_COND_FMT = 0x1c,              /* MIPS3D */ +    C_COND_FMT = 0x3c, + +    CMP_CONDN_S = 0x5, +    CMP_CONDN_D = 0x15 +}; + +/* POOL32Fxf encoding of minor opcode extension field */ + +enum { +    CVT_L = 0x04, +    RSQRT_FMT = 0x08, +    FLOOR_L = 0x0c, +    CVT_PW_PS = 0x1c, +    CVT_W = 0x24, +    SQRT_FMT = 0x28, +    FLOOR_W = 0x2c, +    CVT_PS_PW = 0x3c, +    CFC1 = 0x40, +    RECIP_FMT = 0x48, +    CEIL_L = 0x4c, +    CTC1 = 0x60, +    CEIL_W = 0x6c, +    MFC1 = 0x80, +    CVT_S_PL = 0x84, +    TRUNC_L = 0x8c, +    MTC1 = 0xa0, +    CVT_S_PU = 0xa4, +    TRUNC_W = 0xac, +    MFHC1 = 0xc0, +    ROUND_L = 0xcc, +    MTHC1 = 0xe0, +    ROUND_W = 0xec, + +    MOV_FMT = 0x01, +    MOVF = 0x05, +    ABS_FMT = 0x0d, +    RSQRT1_FMT = 0x1d, +    MOVT = 0x25, +    NEG_FMT = 0x2d, +    CVT_D = 0x4d, +    RECIP1_FMT = 0x5d, +    CVT_S = 0x6d +}; + +/* POOL32I encoding of minor opcode field (bits 25..21) */ + +enum { +    BLTZ = 0x00, +    BLTZAL = 0x01, +    BGEZ = 0x02, +    BGEZAL = 0x03, +    BLEZ = 0x04, +    BNEZC = 0x05, +    BGTZ = 0x06, +    BEQZC = 0x07, +    TLTI = 0x08, +    BC1EQZC = 0x08, +    TGEI = 0x09, +    BC1NEZC = 0x09, +    TLTIU = 0x0a, +    BC2EQZC = 0x0a, +    TGEIU = 0x0b, +    BC2NEZC = 0x0a, +    TNEI = 0x0c, +    R6_SYNCI = 0x0c, +    LUI = 0x0d, +    TEQI = 0x0e, +    SYNCI = 0x10, +    BLTZALS = 0x11, +    BGEZALS = 0x13, +    BC2F = 0x14, +    BC2T = 0x15, +    BPOSGE64 = 0x1a, +    BPOSGE32 = 0x1b, +    /* These overlap and are distinguished by bit16 of the instruction */ +    BC1F = 0x1c, +    BC1T = 0x1d, +    BC1ANY2F = 0x1c, +    BC1ANY2T = 0x1d, +    BC1ANY4F = 0x1e, +    BC1ANY4T = 0x1f +}; + +/* POOL16A encoding of minor opcode field */ + +enum { +    ADDU16 = 0x0, +    SUBU16 = 0x1 +}; + +/* POOL16B encoding of minor opcode field */ + +enum { +    SLL16 = 0x0, +    SRL16 = 0x1 +}; + +/* POOL16C encoding of minor opcode field */ + +enum { +    NOT16 = 0x00, +    XOR16 = 0x04, +    AND16 = 0x08, +    OR16 = 0x0c, +    LWM16 = 0x10, +    SWM16 = 0x14, +    JR16 = 0x18, +    JRC16 = 0x1a, +    JALR16 = 0x1c, +    JALR16S = 0x1e, +    MFHI16 = 0x20, +    MFLO16 = 0x24, +    BREAK16 = 0x28, +    SDBBP16 = 0x2c, +    JRADDIUSP = 0x30 +}; + +/* R6 POOL16C encoding of minor opcode field (bits 0..5) */ + +enum { +    R6_NOT16    = 0x00, +    R6_AND16    = 0x01, +    R6_LWM16    = 0x02, +    R6_JRC16    = 0x03, +    MOVEP       = 0x04, +    MOVEP_07    = 0x07, +    R6_XOR16    = 0x08, +    R6_OR16     = 0x09, +    R6_SWM16    = 0x0a, +    JALRC16     = 0x0b, +    MOVEP_0C    = 0x0c, +    MOVEP_0F    = 0x0f, +    JRCADDIUSP  = 0x13, +    R6_BREAK16  = 0x1b, +    R6_SDBBP16  = 0x3b +}; + +/* POOL16D encoding of minor opcode field */ + +enum { +    ADDIUS5 = 0x0, +    ADDIUSP = 0x1 +}; + +/* POOL16E encoding of minor opcode field */ + +enum { +    ADDIUR2 = 0x0, +    ADDIUR1SP = 0x1 +}; + +static int mmreg (int r) +{ +    static const int map[] = { 16, 17, 2, 3, 4, 5, 6, 7 }; + +    return map[r]; +} + +/* Used for 16-bit store instructions.  */ +static int mmreg2 (int r) +{ +    static const int map[] = { 0, 17, 2, 3, 4, 5, 6, 7 }; + +    return map[r]; +} + +#define uMIPS_RD(op) ((op >> 7) & 0x7) +#define uMIPS_RS(op) ((op >> 4) & 0x7) +#define uMIPS_RS2(op) uMIPS_RS(op) +#define uMIPS_RS1(op) ((op >> 1) & 0x7) +#define uMIPS_RD5(op) ((op >> 5) & 0x1f) +#define uMIPS_RS5(op) (op & 0x1f) + +/* Signed immediate */ +#define SIMM(op, start, width)                                          \ +    ((int32_t)(((op >> start) & ((~0U) >> (32-width)))                 \ +               << (32-width))                                           \ +     >> (32-width)) +/* Zero-extended immediate */ +#define ZIMM(op, start, width) ((op >> start) & ((~0U) >> (32-width))) + +static void gen_addiur1sp(DisasContext *ctx) +{ +    int rd = mmreg(uMIPS_RD(ctx->opcode)); + +    gen_arith_imm(ctx, OPC_ADDIU, rd, 29, ((ctx->opcode >> 1) & 0x3f) << 2); +} + +static void gen_addiur2(DisasContext *ctx) +{ +    static const int decoded_imm[] = { 1, 4, 8, 12, 16, 20, 24, -1 }; +    int rd = mmreg(uMIPS_RD(ctx->opcode)); +    int rs = mmreg(uMIPS_RS(ctx->opcode)); + +    gen_arith_imm(ctx, OPC_ADDIU, rd, rs, decoded_imm[ZIMM(ctx->opcode, 1, 3)]); +} + +static void gen_addiusp(DisasContext *ctx) +{ +    int encoded = ZIMM(ctx->opcode, 1, 9); +    int decoded; + +    if (encoded <= 1) { +        decoded = 256 + encoded; +    } else if (encoded <= 255) { +        decoded = encoded; +    } else if (encoded <= 509) { +        decoded = encoded - 512; +    } else { +        decoded = encoded - 768; +    } + +    gen_arith_imm(ctx, OPC_ADDIU, 29, 29, decoded << 2); +} + +static void gen_addius5(DisasContext *ctx) +{ +    int imm = SIMM(ctx->opcode, 1, 4); +    int rd = (ctx->opcode >> 5) & 0x1f; + +    gen_arith_imm(ctx, OPC_ADDIU, rd, rd, imm); +} + +static void gen_andi16(DisasContext *ctx) +{ +    static const int decoded_imm[] = { 128, 1, 2, 3, 4, 7, 8, 15, 16, +                                 31, 32, 63, 64, 255, 32768, 65535 }; +    int rd = mmreg(uMIPS_RD(ctx->opcode)); +    int rs = mmreg(uMIPS_RS(ctx->opcode)); +    int encoded = ZIMM(ctx->opcode, 0, 4); + +    gen_logic_imm(ctx, OPC_ANDI, rd, rs, decoded_imm[encoded]); +} + +static void gen_ldst_multiple (DisasContext *ctx, uint32_t opc, int reglist, +                               int base, int16_t offset) +{ +    const char *opn = "ldst_multiple"; +    TCGv t0, t1; +    TCGv_i32 t2; + +    if (ctx->hflags & MIPS_HFLAG_BMASK) { +        generate_exception(ctx, EXCP_RI); +        return; +    } + +    t0 = tcg_temp_new(); + +    gen_base_offset_addr(ctx, t0, base, offset); + +    t1 = tcg_const_tl(reglist); +    t2 = tcg_const_i32(ctx->mem_idx); + +    save_cpu_state(ctx, 1); +    switch (opc) { +    case LWM32: +        gen_helper_lwm(cpu_env, t0, t1, t2); +        opn = "lwm"; +        break; +    case SWM32: +        gen_helper_swm(cpu_env, t0, t1, t2); +        opn = "swm"; +        break; +#ifdef TARGET_MIPS64 +    case LDM: +        gen_helper_ldm(cpu_env, t0, t1, t2); +        opn = "ldm"; +        break; +    case SDM: +        gen_helper_sdm(cpu_env, t0, t1, t2); +        opn = "sdm"; +        break; +#endif +    } +    (void)opn; +    MIPS_DEBUG("%s, %x, %d(%s)", opn, reglist, offset, regnames[base]); +    tcg_temp_free(t0); +    tcg_temp_free(t1); +    tcg_temp_free_i32(t2); +} + + +static void gen_pool16c_insn(DisasContext *ctx) +{ +    int rd = mmreg((ctx->opcode >> 3) & 0x7); +    int rs = mmreg(ctx->opcode & 0x7); + +    switch (((ctx->opcode) >> 4) & 0x3f) { +    case NOT16 + 0: +    case NOT16 + 1: +    case NOT16 + 2: +    case NOT16 + 3: +        gen_logic(ctx, OPC_NOR, rd, rs, 0); +        break; +    case XOR16 + 0: +    case XOR16 + 1: +    case XOR16 + 2: +    case XOR16 + 3: +        gen_logic(ctx, OPC_XOR, rd, rd, rs); +        break; +    case AND16 + 0: +    case AND16 + 1: +    case AND16 + 2: +    case AND16 + 3: +        gen_logic(ctx, OPC_AND, rd, rd, rs); +        break; +    case OR16 + 0: +    case OR16 + 1: +    case OR16 + 2: +    case OR16 + 3: +        gen_logic(ctx, OPC_OR, rd, rd, rs); +        break; +    case LWM16 + 0: +    case LWM16 + 1: +    case LWM16 + 2: +    case LWM16 + 3: +        { +            static const int lwm_convert[] = { 0x11, 0x12, 0x13, 0x14 }; +            int offset = ZIMM(ctx->opcode, 0, 4); + +            gen_ldst_multiple(ctx, LWM32, lwm_convert[(ctx->opcode >> 4) & 0x3], +                              29, offset << 2); +        } +        break; +    case SWM16 + 0: +    case SWM16 + 1: +    case SWM16 + 2: +    case SWM16 + 3: +        { +            static const int swm_convert[] = { 0x11, 0x12, 0x13, 0x14 }; +            int offset = ZIMM(ctx->opcode, 0, 4); + +            gen_ldst_multiple(ctx, SWM32, swm_convert[(ctx->opcode >> 4) & 0x3], +                              29, offset << 2); +        } +        break; +    case JR16 + 0: +    case JR16 + 1: +        { +            int reg = ctx->opcode & 0x1f; + +            gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0, 4); +        } +        break; +    case JRC16 + 0: +    case JRC16 + 1: +        { +            int reg = ctx->opcode & 0x1f; +            gen_compute_branch(ctx, OPC_JR, 2, reg, 0, 0, 0); +            /* Let normal delay slot handling in our caller take us +               to the branch target.  */ +        } +        break; +    case JALR16 + 0: +    case JALR16 + 1: +        gen_compute_branch(ctx, OPC_JALR, 2, ctx->opcode & 0x1f, 31, 0, 4); +        ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +        break; +    case JALR16S + 0: +    case JALR16S + 1: +        gen_compute_branch(ctx, OPC_JALR, 2, ctx->opcode & 0x1f, 31, 0, 2); +        ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +        break; +    case MFHI16 + 0: +    case MFHI16 + 1: +        gen_HILO(ctx, OPC_MFHI, 0, uMIPS_RS5(ctx->opcode)); +        break; +    case MFLO16 + 0: +    case MFLO16 + 1: +        gen_HILO(ctx, OPC_MFLO, 0, uMIPS_RS5(ctx->opcode)); +        break; +    case BREAK16: +        generate_exception(ctx, EXCP_BREAK); +        break; +    case SDBBP16: +        if (is_uhi(extract32(ctx->opcode, 0, 4))) { +            gen_helper_do_semihosting(cpu_env); +        } else { +            /* XXX: not clear which exception should be raised +             *      when in debug mode... +             */ +            check_insn(ctx, ISA_MIPS32); +            generate_exception(ctx, EXCP_DBp); +        } +        break; +    case JRADDIUSP + 0: +    case JRADDIUSP + 1: +        { +            int imm = ZIMM(ctx->opcode, 0, 5); +            gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0, 0); +            gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm << 2); +            /* Let normal delay slot handling in our caller take us +               to the branch target.  */ +        } +        break; +    default: +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static inline void gen_movep(DisasContext *ctx, int enc_dest, int enc_rt, +                             int enc_rs) +{ +    int rd, rs, re, rt; +    static const int rd_enc[] = { 5, 5, 6, 4, 4, 4, 4, 4 }; +    static const int re_enc[] = { 6, 7, 7, 21, 22, 5, 6, 7 }; +    static const int rs_rt_enc[] = { 0, 17, 2, 3, 16, 18, 19, 20 }; +    rd = rd_enc[enc_dest]; +    re = re_enc[enc_dest]; +    rs = rs_rt_enc[enc_rs]; +    rt = rs_rt_enc[enc_rt]; +    if (rs) { +        tcg_gen_mov_tl(cpu_gpr[rd], cpu_gpr[rs]); +    } else { +        tcg_gen_movi_tl(cpu_gpr[rd], 0); +    } +    if (rt) { +        tcg_gen_mov_tl(cpu_gpr[re], cpu_gpr[rt]); +    } else { +        tcg_gen_movi_tl(cpu_gpr[re], 0); +    } +} + +static void gen_pool16c_r6_insn(DisasContext *ctx) +{ +    int rt = mmreg((ctx->opcode >> 7) & 0x7); +    int rs = mmreg((ctx->opcode >> 4) & 0x7); + +    switch (ctx->opcode & 0xf) { +    case R6_NOT16: +        gen_logic(ctx, OPC_NOR, rt, rs, 0); +        break; +    case R6_AND16: +        gen_logic(ctx, OPC_AND, rt, rt, rs); +        break; +    case R6_LWM16: +        { +            int lwm_converted = 0x11 + extract32(ctx->opcode, 8, 2); +            int offset = extract32(ctx->opcode, 4, 4); +            gen_ldst_multiple(ctx, LWM32, lwm_converted, 29, offset << 2); +        } +        break; +    case R6_JRC16: /* JRCADDIUSP */ +        if ((ctx->opcode >> 4) & 1) { +            /* JRCADDIUSP */ +            int imm = extract32(ctx->opcode, 5, 5); +            gen_compute_branch(ctx, OPC_JR, 2, 31, 0, 0, 0); +            gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm << 2); +        } else { +            /* JRC16 */ +            int rs = extract32(ctx->opcode, 5, 5); +            gen_compute_branch(ctx, OPC_JR, 2, rs, 0, 0, 0); +        } +        break; +    case MOVEP ... MOVEP_07: +    case MOVEP_0C ... MOVEP_0F: +        { +            int enc_dest = uMIPS_RD(ctx->opcode); +            int enc_rt = uMIPS_RS2(ctx->opcode); +            int enc_rs = (ctx->opcode & 3) | ((ctx->opcode >> 1) & 4); +            gen_movep(ctx, enc_dest, enc_rt, enc_rs); +        } +        break; +    case R6_XOR16: +        gen_logic(ctx, OPC_XOR, rt, rt, rs); +        break; +    case R6_OR16: +        gen_logic(ctx, OPC_OR, rt, rt, rs); +        break; +    case R6_SWM16: +        { +            int swm_converted = 0x11 + extract32(ctx->opcode, 8, 2); +            int offset = extract32(ctx->opcode, 4, 4); +            gen_ldst_multiple(ctx, SWM32, swm_converted, 29, offset << 2); +        } +        break; +    case JALRC16: /* BREAK16, SDBBP16 */ +        switch (ctx->opcode & 0x3f) { +        case JALRC16: +        case JALRC16 + 0x20: +            /* JALRC16 */ +            gen_compute_branch(ctx, OPC_JALR, 2, (ctx->opcode >> 5) & 0x1f, +                               31, 0, 0); +            break; +        case R6_BREAK16: +            /* BREAK16 */ +            generate_exception(ctx, EXCP_BREAK); +            break; +        case R6_SDBBP16: +            /* SDBBP16 */ +            if (is_uhi(extract32(ctx->opcode, 6, 4))) { +                gen_helper_do_semihosting(cpu_env); +            } else { +                if (ctx->hflags & MIPS_HFLAG_SBRI) { +                    generate_exception(ctx, EXCP_RI); +                } else { +                    generate_exception(ctx, EXCP_DBp); +                } +            } +            break; +        } +        break; +    default: +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static void gen_ldxs (DisasContext *ctx, int base, int index, int rd) +{ +    TCGv t0 = tcg_temp_new(); +    TCGv t1 = tcg_temp_new(); + +    gen_load_gpr(t0, base); + +    if (index != 0) { +        gen_load_gpr(t1, index); +        tcg_gen_shli_tl(t1, t1, 2); +        gen_op_addr_add(ctx, t0, t1, t0); +    } + +    tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); +    gen_store_gpr(t1, rd); + +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +static void gen_ldst_pair (DisasContext *ctx, uint32_t opc, int rd, +                           int base, int16_t offset) +{ +    const char *opn = "ldst_pair"; +    TCGv t0, t1; + +    if (ctx->hflags & MIPS_HFLAG_BMASK || rd == 31) { +        generate_exception(ctx, EXCP_RI); +        return; +    } + +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); + +    gen_base_offset_addr(ctx, t0, base, offset); + +    switch (opc) { +    case LWP: +        if (rd == base) { +            generate_exception(ctx, EXCP_RI); +            return; +        } +        tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); +        gen_store_gpr(t1, rd); +        tcg_gen_movi_tl(t1, 4); +        gen_op_addr_add(ctx, t0, t0, t1); +        tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TESL); +        gen_store_gpr(t1, rd+1); +        opn = "lwp"; +        break; +    case SWP: +        gen_load_gpr(t1, rd); +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); +        tcg_gen_movi_tl(t1, 4); +        gen_op_addr_add(ctx, t0, t0, t1); +        gen_load_gpr(t1, rd+1); +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); +        opn = "swp"; +        break; +#ifdef TARGET_MIPS64 +    case LDP: +        if (rd == base) { +            generate_exception(ctx, EXCP_RI); +            return; +        } +        tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEQ); +        gen_store_gpr(t1, rd); +        tcg_gen_movi_tl(t1, 8); +        gen_op_addr_add(ctx, t0, t0, t1); +        tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_TEQ); +        gen_store_gpr(t1, rd+1); +        opn = "ldp"; +        break; +    case SDP: +        gen_load_gpr(t1, rd); +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ); +        tcg_gen_movi_tl(t1, 8); +        gen_op_addr_add(ctx, t0, t0, t1); +        gen_load_gpr(t1, rd+1); +        tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEQ); +        opn = "sdp"; +        break; +#endif +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s, %s, %d(%s)", opn, regnames[rd], offset, regnames[base]); +    tcg_temp_free(t0); +    tcg_temp_free(t1); +} + +static void gen_pool32axf (CPUMIPSState *env, DisasContext *ctx, int rt, int rs) +{ +    int extension = (ctx->opcode >> 6) & 0x3f; +    int minor = (ctx->opcode >> 12) & 0xf; +    uint32_t mips32_op; + +    switch (extension) { +    case TEQ: +        mips32_op = OPC_TEQ; +        goto do_trap; +    case TGE: +        mips32_op = OPC_TGE; +        goto do_trap; +    case TGEU: +        mips32_op = OPC_TGEU; +        goto do_trap; +    case TLT: +        mips32_op = OPC_TLT; +        goto do_trap; +    case TLTU: +        mips32_op = OPC_TLTU; +        goto do_trap; +    case TNE: +        mips32_op = OPC_TNE; +    do_trap: +        gen_trap(ctx, mips32_op, rs, rt, -1); +        break; +#ifndef CONFIG_USER_ONLY +    case MFC0: +    case MFC0 + 32: +        check_cp0_enabled(ctx); +        if (rt == 0) { +            /* Treat as NOP. */ +            break; +        } +        gen_mfc0(ctx, cpu_gpr[rt], rs, (ctx->opcode >> 11) & 0x7); +        break; +    case MTC0: +    case MTC0 + 32: +        check_cp0_enabled(ctx); +        { +            TCGv t0 = tcg_temp_new(); + +            gen_load_gpr(t0, rt); +            gen_mtc0(ctx, t0, rs, (ctx->opcode >> 11) & 0x7); +            tcg_temp_free(t0); +        } +        break; +#endif +    case 0x2a: +        switch (minor & 3) { +        case MADD_ACC: +            gen_muldiv(ctx, OPC_MADD, (ctx->opcode >> 14) & 3, rs, rt); +            break; +        case MADDU_ACC: +            gen_muldiv(ctx, OPC_MADDU, (ctx->opcode >> 14) & 3, rs, rt); +            break; +        case MSUB_ACC: +            gen_muldiv(ctx, OPC_MSUB, (ctx->opcode >> 14) & 3, rs, rt); +            break; +        case MSUBU_ACC: +            gen_muldiv(ctx, OPC_MSUBU, (ctx->opcode >> 14) & 3, rs, rt); +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +    case 0x32: +        switch (minor & 3) { +        case MULT_ACC: +            gen_muldiv(ctx, OPC_MULT, (ctx->opcode >> 14) & 3, rs, rt); +            break; +        case MULTU_ACC: +            gen_muldiv(ctx, OPC_MULTU, (ctx->opcode >> 14) & 3, rs, rt); +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +    case 0x2c: +        switch (minor) { +        case BITSWAP: +            check_insn(ctx, ISA_MIPS32R6); +            gen_bitswap(ctx, OPC_BITSWAP, rs, rt); +            break; +        case SEB: +            gen_bshfl(ctx, OPC_SEB, rs, rt); +            break; +        case SEH: +            gen_bshfl(ctx, OPC_SEH, rs, rt); +            break; +        case CLO: +            mips32_op = OPC_CLO; +            goto do_cl; +        case CLZ: +            mips32_op = OPC_CLZ; +        do_cl: +            check_insn(ctx, ISA_MIPS32); +            gen_cl(ctx, mips32_op, rt, rs); +            break; +        case RDHWR: +            gen_rdhwr(ctx, rt, rs); +            break; +        case WSBH: +            gen_bshfl(ctx, OPC_WSBH, rs, rt); +            break; +        case MULT: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_MULT; +            goto do_mul; +        case MULTU: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_MULTU; +            goto do_mul; +        case DIV: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_DIV; +            goto do_div; +        case DIVU: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_DIVU; +            goto do_div; +        do_div: +            check_insn(ctx, ISA_MIPS32); +            gen_muldiv(ctx, mips32_op, 0, rs, rt); +            break; +        case MADD: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_MADD; +            goto do_mul; +        case MADDU: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_MADDU; +            goto do_mul; +        case MSUB: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_MSUB; +            goto do_mul; +        case MSUBU: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_MSUBU; +        do_mul: +            check_insn(ctx, ISA_MIPS32); +            gen_muldiv(ctx, mips32_op, 0, rs, rt); +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +    case 0x34: +        switch (minor) { +        case MFC2: +        case MTC2: +        case MFHC2: +        case MTHC2: +        case CFC2: +        case CTC2: +            generate_exception_err(ctx, EXCP_CpU, 2); +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +    case 0x3c: +        switch (minor) { +        case JALR:    /* JALRC */ +        case JALR_HB: /* JALRC_HB */ +            if (ctx->insn_flags & ISA_MIPS32R6) { +                /* JALRC, JALRC_HB */ +                gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 0); +            } else { +                /* JALR, JALR_HB */ +                gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 4); +                ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +            } +            break; +        case JALRS: +        case JALRS_HB: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch(ctx, OPC_JALR, 4, rs, rt, 0, 2); +            ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +    case 0x05: +        switch (minor) { +        case RDPGPR: +            check_cp0_enabled(ctx); +            check_insn(ctx, ISA_MIPS32R2); +            gen_load_srsgpr(rs, rt); +            break; +        case WRPGPR: +            check_cp0_enabled(ctx); +            check_insn(ctx, ISA_MIPS32R2); +            gen_store_srsgpr(rs, rt); +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +#ifndef CONFIG_USER_ONLY +    case 0x0d: +        switch (minor) { +        case TLBP: +            mips32_op = OPC_TLBP; +            goto do_cp0; +        case TLBR: +            mips32_op = OPC_TLBR; +            goto do_cp0; +        case TLBWI: +            mips32_op = OPC_TLBWI; +            goto do_cp0; +        case TLBWR: +            mips32_op = OPC_TLBWR; +            goto do_cp0; +        case TLBINV: +            mips32_op = OPC_TLBINV; +            goto do_cp0; +        case TLBINVF: +            mips32_op = OPC_TLBINVF; +            goto do_cp0; +        case WAIT: +            mips32_op = OPC_WAIT; +            goto do_cp0; +        case DERET: +            mips32_op = OPC_DERET; +            goto do_cp0; +        case ERET: +            mips32_op = OPC_ERET; +        do_cp0: +            gen_cp0(env, ctx, mips32_op, rt, rs); +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +    case 0x1d: +        switch (minor) { +        case DI: +            check_cp0_enabled(ctx); +            { +                TCGv t0 = tcg_temp_new(); + +                save_cpu_state(ctx, 1); +                gen_helper_di(t0, cpu_env); +                gen_store_gpr(t0, rs); +                /* Stop translation as we may have switched the execution mode */ +                ctx->bstate = BS_STOP; +                tcg_temp_free(t0); +            } +            break; +        case EI: +            check_cp0_enabled(ctx); +            { +                TCGv t0 = tcg_temp_new(); + +                save_cpu_state(ctx, 1); +                gen_helper_ei(t0, cpu_env); +                gen_store_gpr(t0, rs); +                /* Stop translation as we may have switched the execution mode */ +                ctx->bstate = BS_STOP; +                tcg_temp_free(t0); +            } +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +#endif +    case 0x2d: +        switch (minor) { +        case SYNC: +            /* NOP */ +            break; +        case SYSCALL: +            generate_exception(ctx, EXCP_SYSCALL); +            ctx->bstate = BS_STOP; +            break; +        case SDBBP: +            if (is_uhi(extract32(ctx->opcode, 16, 10))) { +                gen_helper_do_semihosting(cpu_env); +            } else { +                check_insn(ctx, ISA_MIPS32); +                if (ctx->hflags & MIPS_HFLAG_SBRI) { +                    generate_exception(ctx, EXCP_RI); +                } else { +                    generate_exception(ctx, EXCP_DBp); +                } +            } +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +    case 0x01: +        switch (minor & 3) { +        case MFHI_ACC: +            gen_HILO(ctx, OPC_MFHI, minor >> 2, rs); +            break; +        case MFLO_ACC: +            gen_HILO(ctx, OPC_MFLO, minor >> 2, rs); +            break; +        case MTHI_ACC: +            gen_HILO(ctx, OPC_MTHI, minor >> 2, rs); +            break; +        case MTLO_ACC: +            gen_HILO(ctx, OPC_MTLO, minor >> 2, rs); +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +    case 0x35: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        switch (minor) { +        case MFHI32: +            gen_HILO(ctx, OPC_MFHI, 0, rs); +            break; +        case MFLO32: +            gen_HILO(ctx, OPC_MFLO, 0, rs); +            break; +        case MTHI32: +            gen_HILO(ctx, OPC_MTHI, 0, rs); +            break; +        case MTLO32: +            gen_HILO(ctx, OPC_MTLO, 0, rs); +            break; +        default: +            goto pool32axf_invalid; +        } +        break; +    default: +    pool32axf_invalid: +        MIPS_INVAL("pool32axf"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +/* Values for microMIPS fmt field.  Variable-width, depending on which +   formats the instruction supports.  */ + +enum { +    FMT_SD_S = 0, +    FMT_SD_D = 1, + +    FMT_SDPS_S = 0, +    FMT_SDPS_D = 1, +    FMT_SDPS_PS = 2, + +    FMT_SWL_S = 0, +    FMT_SWL_W = 1, +    FMT_SWL_L = 2, + +    FMT_DWL_D = 0, +    FMT_DWL_W = 1, +    FMT_DWL_L = 2 +}; + +static void gen_pool32fxf(DisasContext *ctx, int rt, int rs) +{ +    int extension = (ctx->opcode >> 6) & 0x3ff; +    uint32_t mips32_op; + +#define FLOAT_1BIT_FMT(opc, fmt) (fmt << 8) | opc +#define FLOAT_2BIT_FMT(opc, fmt) (fmt << 7) | opc +#define COND_FLOAT_MOV(opc, cond) (cond << 7) | opc + +    switch (extension) { +    case FLOAT_1BIT_FMT(CFC1, 0): +        mips32_op = OPC_CFC1; +        goto do_cp1; +    case FLOAT_1BIT_FMT(CTC1, 0): +        mips32_op = OPC_CTC1; +        goto do_cp1; +    case FLOAT_1BIT_FMT(MFC1, 0): +        mips32_op = OPC_MFC1; +        goto do_cp1; +    case FLOAT_1BIT_FMT(MTC1, 0): +        mips32_op = OPC_MTC1; +        goto do_cp1; +    case FLOAT_1BIT_FMT(MFHC1, 0): +        mips32_op = OPC_MFHC1; +        goto do_cp1; +    case FLOAT_1BIT_FMT(MTHC1, 0): +        mips32_op = OPC_MTHC1; +    do_cp1: +        gen_cp1(ctx, mips32_op, rt, rs); +        break; + +        /* Reciprocal square root */ +    case FLOAT_1BIT_FMT(RSQRT_FMT, FMT_SD_S): +        mips32_op = OPC_RSQRT_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(RSQRT_FMT, FMT_SD_D): +        mips32_op = OPC_RSQRT_D; +        goto do_unaryfp; + +        /* Square root */ +    case FLOAT_1BIT_FMT(SQRT_FMT, FMT_SD_S): +        mips32_op = OPC_SQRT_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(SQRT_FMT, FMT_SD_D): +        mips32_op = OPC_SQRT_D; +        goto do_unaryfp; + +        /* Reciprocal */ +    case FLOAT_1BIT_FMT(RECIP_FMT, FMT_SD_S): +        mips32_op = OPC_RECIP_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(RECIP_FMT, FMT_SD_D): +        mips32_op = OPC_RECIP_D; +        goto do_unaryfp; + +        /* Floor */ +    case FLOAT_1BIT_FMT(FLOOR_L, FMT_SD_S): +        mips32_op = OPC_FLOOR_L_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(FLOOR_L, FMT_SD_D): +        mips32_op = OPC_FLOOR_L_D; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(FLOOR_W, FMT_SD_S): +        mips32_op = OPC_FLOOR_W_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(FLOOR_W, FMT_SD_D): +        mips32_op = OPC_FLOOR_W_D; +        goto do_unaryfp; + +        /* Ceiling */ +    case FLOAT_1BIT_FMT(CEIL_L, FMT_SD_S): +        mips32_op = OPC_CEIL_L_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(CEIL_L, FMT_SD_D): +        mips32_op = OPC_CEIL_L_D; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(CEIL_W, FMT_SD_S): +        mips32_op = OPC_CEIL_W_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(CEIL_W, FMT_SD_D): +        mips32_op = OPC_CEIL_W_D; +        goto do_unaryfp; + +        /* Truncation */ +    case FLOAT_1BIT_FMT(TRUNC_L, FMT_SD_S): +        mips32_op = OPC_TRUNC_L_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(TRUNC_L, FMT_SD_D): +        mips32_op = OPC_TRUNC_L_D; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(TRUNC_W, FMT_SD_S): +        mips32_op = OPC_TRUNC_W_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(TRUNC_W, FMT_SD_D): +        mips32_op = OPC_TRUNC_W_D; +        goto do_unaryfp; + +        /* Round */ +    case FLOAT_1BIT_FMT(ROUND_L, FMT_SD_S): +        mips32_op = OPC_ROUND_L_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(ROUND_L, FMT_SD_D): +        mips32_op = OPC_ROUND_L_D; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(ROUND_W, FMT_SD_S): +        mips32_op = OPC_ROUND_W_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(ROUND_W, FMT_SD_D): +        mips32_op = OPC_ROUND_W_D; +        goto do_unaryfp; + +        /* Integer to floating-point conversion */ +    case FLOAT_1BIT_FMT(CVT_L, FMT_SD_S): +        mips32_op = OPC_CVT_L_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(CVT_L, FMT_SD_D): +        mips32_op = OPC_CVT_L_D; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(CVT_W, FMT_SD_S): +        mips32_op = OPC_CVT_W_S; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(CVT_W, FMT_SD_D): +        mips32_op = OPC_CVT_W_D; +        goto do_unaryfp; + +        /* Paired-foo conversions */ +    case FLOAT_1BIT_FMT(CVT_S_PL, 0): +        mips32_op = OPC_CVT_S_PL; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(CVT_S_PU, 0): +        mips32_op = OPC_CVT_S_PU; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(CVT_PW_PS, 0): +        mips32_op = OPC_CVT_PW_PS; +        goto do_unaryfp; +    case FLOAT_1BIT_FMT(CVT_PS_PW, 0): +        mips32_op = OPC_CVT_PS_PW; +        goto do_unaryfp; + +        /* Floating-point moves */ +    case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_S): +        mips32_op = OPC_MOV_S; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_D): +        mips32_op = OPC_MOV_D; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(MOV_FMT, FMT_SDPS_PS): +        mips32_op = OPC_MOV_PS; +        goto do_unaryfp; + +        /* Absolute value */ +    case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_S): +        mips32_op = OPC_ABS_S; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_D): +        mips32_op = OPC_ABS_D; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(ABS_FMT, FMT_SDPS_PS): +        mips32_op = OPC_ABS_PS; +        goto do_unaryfp; + +        /* Negation */ +    case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_S): +        mips32_op = OPC_NEG_S; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_D): +        mips32_op = OPC_NEG_D; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(NEG_FMT, FMT_SDPS_PS): +        mips32_op = OPC_NEG_PS; +        goto do_unaryfp; + +        /* Reciprocal square root step */ +    case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_S): +        mips32_op = OPC_RSQRT1_S; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_D): +        mips32_op = OPC_RSQRT1_D; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(RSQRT1_FMT, FMT_SDPS_PS): +        mips32_op = OPC_RSQRT1_PS; +        goto do_unaryfp; + +        /* Reciprocal step */ +    case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_S): +        mips32_op = OPC_RECIP1_S; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_D): +        mips32_op = OPC_RECIP1_S; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(RECIP1_FMT, FMT_SDPS_PS): +        mips32_op = OPC_RECIP1_PS; +        goto do_unaryfp; + +        /* Conversions from double */ +    case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_S): +        mips32_op = OPC_CVT_D_S; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_W): +        mips32_op = OPC_CVT_D_W; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(CVT_D, FMT_SWL_L): +        mips32_op = OPC_CVT_D_L; +        goto do_unaryfp; + +        /* Conversions from single */ +    case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_D): +        mips32_op = OPC_CVT_S_D; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_W): +        mips32_op = OPC_CVT_S_W; +        goto do_unaryfp; +    case FLOAT_2BIT_FMT(CVT_S, FMT_DWL_L): +        mips32_op = OPC_CVT_S_L; +    do_unaryfp: +        gen_farith(ctx, mips32_op, -1, rs, rt, 0); +        break; + +        /* Conditional moves on floating-point codes */ +    case COND_FLOAT_MOV(MOVT, 0): +    case COND_FLOAT_MOV(MOVT, 1): +    case COND_FLOAT_MOV(MOVT, 2): +    case COND_FLOAT_MOV(MOVT, 3): +    case COND_FLOAT_MOV(MOVT, 4): +    case COND_FLOAT_MOV(MOVT, 5): +    case COND_FLOAT_MOV(MOVT, 6): +    case COND_FLOAT_MOV(MOVT, 7): +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        gen_movci(ctx, rt, rs, (ctx->opcode >> 13) & 0x7, 1); +        break; +    case COND_FLOAT_MOV(MOVF, 0): +    case COND_FLOAT_MOV(MOVF, 1): +    case COND_FLOAT_MOV(MOVF, 2): +    case COND_FLOAT_MOV(MOVF, 3): +    case COND_FLOAT_MOV(MOVF, 4): +    case COND_FLOAT_MOV(MOVF, 5): +    case COND_FLOAT_MOV(MOVF, 6): +    case COND_FLOAT_MOV(MOVF, 7): +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        gen_movci(ctx, rt, rs, (ctx->opcode >> 13) & 0x7, 0); +        break; +    default: +        MIPS_INVAL("pool32fxf"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) +{ +    int32_t offset; +    uint16_t insn; +    int rt, rs, rd, rr; +    int16_t imm; +    uint32_t op, minor, mips32_op; +    uint32_t cond, fmt, cc; + +    insn = cpu_lduw_code(env, ctx->pc + 2); +    ctx->opcode = (ctx->opcode << 16) | insn; + +    rt = (ctx->opcode >> 21) & 0x1f; +    rs = (ctx->opcode >> 16) & 0x1f; +    rd = (ctx->opcode >> 11) & 0x1f; +    rr = (ctx->opcode >> 6) & 0x1f; +    imm = (int16_t) ctx->opcode; + +    op = (ctx->opcode >> 26) & 0x3f; +    switch (op) { +    case POOL32A: +        minor = ctx->opcode & 0x3f; +        switch (minor) { +        case 0x00: +            minor = (ctx->opcode >> 6) & 0xf; +            switch (minor) { +            case SLL32: +                mips32_op = OPC_SLL; +                goto do_shifti; +            case SRA: +                mips32_op = OPC_SRA; +                goto do_shifti; +            case SRL32: +                mips32_op = OPC_SRL; +                goto do_shifti; +            case ROTR: +                mips32_op = OPC_ROTR; +            do_shifti: +                gen_shift_imm(ctx, mips32_op, rt, rs, rd); +                break; +            case SELEQZ: +                check_insn(ctx, ISA_MIPS32R6); +                gen_cond_move(ctx, OPC_SELEQZ, rd, rs, rt); +                break; +            case SELNEZ: +                check_insn(ctx, ISA_MIPS32R6); +                gen_cond_move(ctx, OPC_SELNEZ, rd, rs, rt); +                break; +            default: +                goto pool32a_invalid; +            } +            break; +        case 0x10: +            minor = (ctx->opcode >> 6) & 0xf; +            switch (minor) { +                /* Arithmetic */ +            case ADD: +                mips32_op = OPC_ADD; +                goto do_arith; +            case ADDU32: +                mips32_op = OPC_ADDU; +                goto do_arith; +            case SUB: +                mips32_op = OPC_SUB; +                goto do_arith; +            case SUBU32: +                mips32_op = OPC_SUBU; +                goto do_arith; +            case MUL: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_MUL; +            do_arith: +                gen_arith(ctx, mips32_op, rd, rs, rt); +                break; +                /* Shifts */ +            case SLLV: +                mips32_op = OPC_SLLV; +                goto do_shift; +            case SRLV: +                mips32_op = OPC_SRLV; +                goto do_shift; +            case SRAV: +                mips32_op = OPC_SRAV; +                goto do_shift; +            case ROTRV: +                mips32_op = OPC_ROTRV; +            do_shift: +                gen_shift(ctx, mips32_op, rd, rs, rt); +                break; +                /* Logical operations */ +            case AND: +                mips32_op = OPC_AND; +                goto do_logic; +            case OR32: +                mips32_op = OPC_OR; +                goto do_logic; +            case NOR: +                mips32_op = OPC_NOR; +                goto do_logic; +            case XOR32: +                mips32_op = OPC_XOR; +            do_logic: +                gen_logic(ctx, mips32_op, rd, rs, rt); +                break; +                /* Set less than */ +            case SLT: +                mips32_op = OPC_SLT; +                goto do_slt; +            case SLTU: +                mips32_op = OPC_SLTU; +            do_slt: +                gen_slt(ctx, mips32_op, rd, rs, rt); +                break; +            default: +                goto pool32a_invalid; +            } +            break; +        case 0x18: +            minor = (ctx->opcode >> 6) & 0xf; +            switch (minor) { +                /* Conditional moves */ +            case MOVN: /* MUL */ +                if (ctx->insn_flags & ISA_MIPS32R6) { +                    /* MUL */ +                    gen_r6_muldiv(ctx, R6_OPC_MUL, rd, rs, rt); +                } else { +                    /* MOVN */ +                    gen_cond_move(ctx, OPC_MOVN, rd, rs, rt); +                } +                break; +            case MOVZ: /* MUH */ +                if (ctx->insn_flags & ISA_MIPS32R6) { +                    /* MUH */ +                    gen_r6_muldiv(ctx, R6_OPC_MUH, rd, rs, rt); +                } else { +                    /* MOVZ */ +                    gen_cond_move(ctx, OPC_MOVZ, rd, rs, rt); +                } +                break; +            case MULU: +                check_insn(ctx, ISA_MIPS32R6); +                gen_r6_muldiv(ctx, R6_OPC_MULU, rd, rs, rt); +                break; +            case MUHU: +                check_insn(ctx, ISA_MIPS32R6); +                gen_r6_muldiv(ctx, R6_OPC_MUHU, rd, rs, rt); +                break; +            case LWXS: /* DIV */ +                if (ctx->insn_flags & ISA_MIPS32R6) { +                    /* DIV */ +                    gen_r6_muldiv(ctx, R6_OPC_DIV, rd, rs, rt); +                } else { +                    /* LWXS */ +                    gen_ldxs(ctx, rs, rt, rd); +                } +                break; +            case MOD: +                check_insn(ctx, ISA_MIPS32R6); +                gen_r6_muldiv(ctx, R6_OPC_MOD, rd, rs, rt); +                break; +            case R6_DIVU: +                check_insn(ctx, ISA_MIPS32R6); +                gen_r6_muldiv(ctx, R6_OPC_DIVU, rd, rs, rt); +                break; +            case MODU: +                check_insn(ctx, ISA_MIPS32R6); +                gen_r6_muldiv(ctx, R6_OPC_MODU, rd, rs, rt); +                break; +            default: +                goto pool32a_invalid; +            } +            break; +        case INS: +            gen_bitops(ctx, OPC_INS, rt, rs, rr, rd); +            return; +        case LSA: +            check_insn(ctx, ISA_MIPS32R6); +            gen_lsa(ctx, OPC_LSA, rd, rs, rt, +                    extract32(ctx->opcode, 9, 2)); +            break; +        case ALIGN: +            check_insn(ctx, ISA_MIPS32R6); +            gen_align(ctx, OPC_ALIGN, rd, rs, rt, +                      extract32(ctx->opcode, 9, 2)); +            break; +        case EXT: +            gen_bitops(ctx, OPC_EXT, rt, rs, rr, rd); +            return; +        case POOL32AXF: +            gen_pool32axf(env, ctx, rt, rs); +            break; +        case 0x07: +            generate_exception(ctx, EXCP_BREAK); +            break; +        default: +        pool32a_invalid: +                MIPS_INVAL("pool32a"); +                generate_exception(ctx, EXCP_RI); +                break; +        } +        break; +    case POOL32B: +        minor = (ctx->opcode >> 12) & 0xf; +        switch (minor) { +        case CACHE: +            check_cp0_enabled(ctx); +            /* Treat as no-op. */ +            break; +        case LWC2: +        case SWC2: +            /* COP2: Not implemented. */ +            generate_exception_err(ctx, EXCP_CpU, 2); +            break; +#ifdef TARGET_MIPS64 +        case LDP: +        case SDP: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            /* Fallthrough */ +#endif +        case LWP: +        case SWP: +            gen_ldst_pair(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12)); +            break; +#ifdef TARGET_MIPS64 +        case LDM: +        case SDM: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            /* Fallthrough */ +#endif +        case LWM32: +        case SWM32: +            gen_ldst_multiple(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12)); +            break; +        default: +            MIPS_INVAL("pool32b"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case POOL32F: +        if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { +            minor = ctx->opcode & 0x3f; +            check_cp1_enabled(ctx); +            switch (minor) { +            case ALNV_PS: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_ALNV_PS; +                goto do_madd; +            case MADD_S: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_MADD_S; +                goto do_madd; +            case MADD_D: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_MADD_D; +                goto do_madd; +            case MADD_PS: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_MADD_PS; +                goto do_madd; +            case MSUB_S: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_MSUB_S; +                goto do_madd; +            case MSUB_D: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_MSUB_D; +                goto do_madd; +            case MSUB_PS: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_MSUB_PS; +                goto do_madd; +            case NMADD_S: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_NMADD_S; +                goto do_madd; +            case NMADD_D: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_NMADD_D; +                goto do_madd; +            case NMADD_PS: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_NMADD_PS; +                goto do_madd; +            case NMSUB_S: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_NMSUB_S; +                goto do_madd; +            case NMSUB_D: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_NMSUB_D; +                goto do_madd; +            case NMSUB_PS: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                mips32_op = OPC_NMSUB_PS; +            do_madd: +                gen_flt3_arith(ctx, mips32_op, rd, rr, rs, rt); +                break; +            case CABS_COND_FMT: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                cond = (ctx->opcode >> 6) & 0xf; +                cc = (ctx->opcode >> 13) & 0x7; +                fmt = (ctx->opcode >> 10) & 0x3; +                switch (fmt) { +                case 0x0: +                    gen_cmpabs_s(ctx, cond, rt, rs, cc); +                    break; +                case 0x1: +                    gen_cmpabs_d(ctx, cond, rt, rs, cc); +                    break; +                case 0x2: +                    gen_cmpabs_ps(ctx, cond, rt, rs, cc); +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +            case C_COND_FMT: +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                cond = (ctx->opcode >> 6) & 0xf; +                cc = (ctx->opcode >> 13) & 0x7; +                fmt = (ctx->opcode >> 10) & 0x3; +                switch (fmt) { +                case 0x0: +                    gen_cmp_s(ctx, cond, rt, rs, cc); +                    break; +                case 0x1: +                    gen_cmp_d(ctx, cond, rt, rs, cc); +                    break; +                case 0x2: +                    gen_cmp_ps(ctx, cond, rt, rs, cc); +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +            case CMP_CONDN_S: +                check_insn(ctx, ISA_MIPS32R6); +                gen_r6_cmp_s(ctx, (ctx->opcode >> 6) & 0x1f, rt, rs, rd); +                break; +            case CMP_CONDN_D: +                check_insn(ctx, ISA_MIPS32R6); +                gen_r6_cmp_d(ctx, (ctx->opcode >> 6) & 0x1f, rt, rs, rd); +                break; +            case POOL32FXF: +                gen_pool32fxf(ctx, rt, rs); +                break; +            case 0x00: +                /* PLL foo */ +                switch ((ctx->opcode >> 6) & 0x7) { +                case PLL_PS: +                    mips32_op = OPC_PLL_PS; +                    goto do_ps; +                case PLU_PS: +                    mips32_op = OPC_PLU_PS; +                    goto do_ps; +                case PUL_PS: +                    mips32_op = OPC_PUL_PS; +                    goto do_ps; +                case PUU_PS: +                    mips32_op = OPC_PUU_PS; +                    goto do_ps; +                case CVT_PS_S: +                    check_insn_opc_removed(ctx, ISA_MIPS32R6); +                    mips32_op = OPC_CVT_PS_S; +                do_ps: +                    gen_farith(ctx, mips32_op, rt, rs, rd, 0); +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +            case MIN_FMT: +                check_insn(ctx, ISA_MIPS32R6); +                switch ((ctx->opcode >> 9) & 0x3) { +                case FMT_SDPS_S: +                    gen_farith(ctx, OPC_MIN_S, rt, rs, rd, 0); +                    break; +                case FMT_SDPS_D: +                    gen_farith(ctx, OPC_MIN_D, rt, rs, rd, 0); +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +            case 0x08: +                /* [LS][WDU]XC1 */ +                switch ((ctx->opcode >> 6) & 0x7) { +                case LWXC1: +                    check_insn_opc_removed(ctx, ISA_MIPS32R6); +                    mips32_op = OPC_LWXC1; +                    goto do_ldst_cp1; +                case SWXC1: +                    check_insn_opc_removed(ctx, ISA_MIPS32R6); +                    mips32_op = OPC_SWXC1; +                    goto do_ldst_cp1; +                case LDXC1: +                    check_insn_opc_removed(ctx, ISA_MIPS32R6); +                    mips32_op = OPC_LDXC1; +                    goto do_ldst_cp1; +                case SDXC1: +                    check_insn_opc_removed(ctx, ISA_MIPS32R6); +                    mips32_op = OPC_SDXC1; +                    goto do_ldst_cp1; +                case LUXC1: +                    check_insn_opc_removed(ctx, ISA_MIPS32R6); +                    mips32_op = OPC_LUXC1; +                    goto do_ldst_cp1; +                case SUXC1: +                    check_insn_opc_removed(ctx, ISA_MIPS32R6); +                    mips32_op = OPC_SUXC1; +                do_ldst_cp1: +                    gen_flt3_ldst(ctx, mips32_op, rd, rd, rt, rs); +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +            case MAX_FMT: +                check_insn(ctx, ISA_MIPS32R6); +                switch ((ctx->opcode >> 9) & 0x3) { +                case FMT_SDPS_S: +                    gen_farith(ctx, OPC_MAX_S, rt, rs, rd, 0); +                    break; +                case FMT_SDPS_D: +                    gen_farith(ctx, OPC_MAX_D, rt, rs, rd, 0); +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +            case 0x18: +                /* 3D insns */ +                check_insn_opc_removed(ctx, ISA_MIPS32R6); +                fmt = (ctx->opcode >> 9) & 0x3; +                switch ((ctx->opcode >> 6) & 0x7) { +                case RSQRT2_FMT: +                    switch (fmt) { +                    case FMT_SDPS_S: +                        mips32_op = OPC_RSQRT2_S; +                        goto do_3d; +                    case FMT_SDPS_D: +                        mips32_op = OPC_RSQRT2_D; +                        goto do_3d; +                    case FMT_SDPS_PS: +                        mips32_op = OPC_RSQRT2_PS; +                        goto do_3d; +                    default: +                        goto pool32f_invalid; +                    } +                    break; +                case RECIP2_FMT: +                    switch (fmt) { +                    case FMT_SDPS_S: +                        mips32_op = OPC_RECIP2_S; +                        goto do_3d; +                    case FMT_SDPS_D: +                        mips32_op = OPC_RECIP2_D; +                        goto do_3d; +                    case FMT_SDPS_PS: +                        mips32_op = OPC_RECIP2_PS; +                        goto do_3d; +                    default: +                        goto pool32f_invalid; +                    } +                    break; +                case ADDR_PS: +                    mips32_op = OPC_ADDR_PS; +                    goto do_3d; +                case MULR_PS: +                    mips32_op = OPC_MULR_PS; +                do_3d: +                    gen_farith(ctx, mips32_op, rt, rs, rd, 0); +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +            case 0x20: +                /* MOV[FT].fmt, PREFX, RINT.fmt, CLASS.fmt*/ +                cc = (ctx->opcode >> 13) & 0x7; +                fmt = (ctx->opcode >> 9) & 0x3; +                switch ((ctx->opcode >> 6) & 0x7) { +                case MOVF_FMT: /* RINT_FMT */ +                    if (ctx->insn_flags & ISA_MIPS32R6) { +                        /* RINT_FMT */ +                        switch (fmt) { +                        case FMT_SDPS_S: +                            gen_farith(ctx, OPC_RINT_S, 0, rt, rs, 0); +                            break; +                        case FMT_SDPS_D: +                            gen_farith(ctx, OPC_RINT_D, 0, rt, rs, 0); +                            break; +                        default: +                            goto pool32f_invalid; +                        } +                    } else { +                        /* MOVF_FMT */ +                        switch (fmt) { +                        case FMT_SDPS_S: +                            gen_movcf_s(ctx, rs, rt, cc, 0); +                            break; +                        case FMT_SDPS_D: +                            gen_movcf_d(ctx, rs, rt, cc, 0); +                            break; +                        case FMT_SDPS_PS: +                            check_ps(ctx); +                            gen_movcf_ps(ctx, rs, rt, cc, 0); +                            break; +                        default: +                            goto pool32f_invalid; +                        } +                    } +                    break; +                case MOVT_FMT: /* CLASS_FMT */ +                    if (ctx->insn_flags & ISA_MIPS32R6) { +                        /* CLASS_FMT */ +                        switch (fmt) { +                        case FMT_SDPS_S: +                            gen_farith(ctx, OPC_CLASS_S, 0, rt, rs, 0); +                            break; +                        case FMT_SDPS_D: +                            gen_farith(ctx, OPC_CLASS_D, 0, rt, rs, 0); +                            break; +                        default: +                            goto pool32f_invalid; +                        } +                    } else { +                        /* MOVT_FMT */ +                        switch (fmt) { +                        case FMT_SDPS_S: +                            gen_movcf_s(ctx, rs, rt, cc, 1); +                            break; +                        case FMT_SDPS_D: +                            gen_movcf_d(ctx, rs, rt, cc, 1); +                            break; +                        case FMT_SDPS_PS: +                            check_ps(ctx); +                            gen_movcf_ps(ctx, rs, rt, cc, 1); +                            break; +                        default: +                            goto pool32f_invalid; +                        } +                    } +                    break; +                case PREFX: +                    check_insn_opc_removed(ctx, ISA_MIPS32R6); +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +#define FINSN_3ARG_SDPS(prfx)                           \ +                switch ((ctx->opcode >> 8) & 0x3) {     \ +                case FMT_SDPS_S:                        \ +                    mips32_op = OPC_##prfx##_S;         \ +                    goto do_fpop;                       \ +                case FMT_SDPS_D:                        \ +                    mips32_op = OPC_##prfx##_D;         \ +                    goto do_fpop;                       \ +                case FMT_SDPS_PS:                       \ +                    check_ps(ctx);                      \ +                    mips32_op = OPC_##prfx##_PS;        \ +                    goto do_fpop;                       \ +                default:                                \ +                    goto pool32f_invalid;               \ +                } +            case MINA_FMT: +                check_insn(ctx, ISA_MIPS32R6); +                switch ((ctx->opcode >> 9) & 0x3) { +                case FMT_SDPS_S: +                    gen_farith(ctx, OPC_MINA_S, rt, rs, rd, 0); +                    break; +                case FMT_SDPS_D: +                    gen_farith(ctx, OPC_MINA_D, rt, rs, rd, 0); +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +            case MAXA_FMT: +                check_insn(ctx, ISA_MIPS32R6); +                switch ((ctx->opcode >> 9) & 0x3) { +                case FMT_SDPS_S: +                    gen_farith(ctx, OPC_MAXA_S, rt, rs, rd, 0); +                    break; +                case FMT_SDPS_D: +                    gen_farith(ctx, OPC_MAXA_D, rt, rs, rd, 0); +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +            case 0x30: +                /* regular FP ops */ +                switch ((ctx->opcode >> 6) & 0x3) { +                case ADD_FMT: +                    FINSN_3ARG_SDPS(ADD); +                    break; +                case SUB_FMT: +                    FINSN_3ARG_SDPS(SUB); +                    break; +                case MUL_FMT: +                    FINSN_3ARG_SDPS(MUL); +                    break; +                case DIV_FMT: +                    fmt = (ctx->opcode >> 8) & 0x3; +                    if (fmt == 1) { +                        mips32_op = OPC_DIV_D; +                    } else if (fmt == 0) { +                        mips32_op = OPC_DIV_S; +                    } else { +                        goto pool32f_invalid; +                    } +                    goto do_fpop; +                default: +                    goto pool32f_invalid; +                } +                break; +            case 0x38: +                /* cmovs */ +                switch ((ctx->opcode >> 6) & 0x7) { +                case MOVN_FMT: /* SELNEZ_FMT */ +                    if (ctx->insn_flags & ISA_MIPS32R6) { +                        /* SELNEZ_FMT */ +                        switch ((ctx->opcode >> 9) & 0x3) { +                        case FMT_SDPS_S: +                            gen_sel_s(ctx, OPC_SELNEZ_S, rd, rt, rs); +                            break; +                        case FMT_SDPS_D: +                            gen_sel_d(ctx, OPC_SELNEZ_D, rd, rt, rs); +                            break; +                        default: +                            goto pool32f_invalid; +                        } +                    } else { +                        /* MOVN_FMT */ +                        FINSN_3ARG_SDPS(MOVN); +                    } +                    break; +                case MOVN_FMT_04: +                    check_insn_opc_removed(ctx, ISA_MIPS32R6); +                    FINSN_3ARG_SDPS(MOVN); +                    break; +                case MOVZ_FMT: /* SELEQZ_FMT */ +                    if (ctx->insn_flags & ISA_MIPS32R6) { +                        /* SELEQZ_FMT */ +                        switch ((ctx->opcode >> 9) & 0x3) { +                        case FMT_SDPS_S: +                            gen_sel_s(ctx, OPC_SELEQZ_S, rd, rt, rs); +                            break; +                        case FMT_SDPS_D: +                            gen_sel_d(ctx, OPC_SELEQZ_D, rd, rt, rs); +                            break; +                        default: +                            goto pool32f_invalid; +                        } +                    } else { +                        /* MOVZ_FMT */ +                        FINSN_3ARG_SDPS(MOVZ); +                    } +                    break; +                case MOVZ_FMT_05: +                    check_insn_opc_removed(ctx, ISA_MIPS32R6); +                    FINSN_3ARG_SDPS(MOVZ); +                    break; +                case SEL_FMT: +                    check_insn(ctx, ISA_MIPS32R6); +                    switch ((ctx->opcode >> 9) & 0x3) { +                    case FMT_SDPS_S: +                        gen_sel_s(ctx, OPC_SEL_S, rd, rt, rs); +                        break; +                    case FMT_SDPS_D: +                        gen_sel_d(ctx, OPC_SEL_D, rd, rt, rs); +                        break; +                    default: +                        goto pool32f_invalid; +                    } +                    break; +                case MADDF_FMT: +                    check_insn(ctx, ISA_MIPS32R6); +                    switch ((ctx->opcode >> 9) & 0x3) { +                    case FMT_SDPS_S: +                        mips32_op = OPC_MADDF_S; +                        goto do_fpop; +                    case FMT_SDPS_D: +                        mips32_op = OPC_MADDF_D; +                        goto do_fpop; +                    default: +                        goto pool32f_invalid; +                    } +                    break; +                case MSUBF_FMT: +                    check_insn(ctx, ISA_MIPS32R6); +                    switch ((ctx->opcode >> 9) & 0x3) { +                    case FMT_SDPS_S: +                        mips32_op = OPC_MSUBF_S; +                        goto do_fpop; +                    case FMT_SDPS_D: +                        mips32_op = OPC_MSUBF_D; +                        goto do_fpop; +                    default: +                        goto pool32f_invalid; +                    } +                    break; +                default: +                    goto pool32f_invalid; +                } +                break; +            do_fpop: +                gen_farith(ctx, mips32_op, rt, rs, rd, 0); +                break; +            default: +            pool32f_invalid: +                MIPS_INVAL("pool32f"); +                generate_exception(ctx, EXCP_RI); +                break; +            } +        } else { +            generate_exception_err(ctx, EXCP_CpU, 1); +        } +        break; +    case POOL32I: +        minor = (ctx->opcode >> 21) & 0x1f; +        switch (minor) { +        case BLTZ: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch(ctx, OPC_BLTZ, 4, rs, -1, imm << 1, 4); +            break; +        case BLTZAL: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch(ctx, OPC_BLTZAL, 4, rs, -1, imm << 1, 4); +            ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +            break; +        case BLTZALS: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch(ctx, OPC_BLTZAL, 4, rs, -1, imm << 1, 2); +            ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +            break; +        case BGEZ: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch(ctx, OPC_BGEZ, 4, rs, -1, imm << 1, 4); +            break; +        case BGEZAL: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch(ctx, OPC_BGEZAL, 4, rs, -1, imm << 1, 4); +            ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +            break; +        case BGEZALS: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch(ctx, OPC_BGEZAL, 4, rs, -1, imm << 1, 2); +            ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +            break; +        case BLEZ: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch(ctx, OPC_BLEZ, 4, rs, -1, imm << 1, 4); +            break; +        case BGTZ: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch(ctx, OPC_BGTZ, 4, rs, -1, imm << 1, 4); +            break; + +            /* Traps */ +        case TLTI: /* BC1EQZC */ +            if (ctx->insn_flags & ISA_MIPS32R6) { +                /* BC1EQZC */ +                check_cp1_enabled(ctx); +                gen_compute_branch1_r6(ctx, OPC_BC1EQZ, rs, imm << 1, 0); +            } else { +                /* TLTI */ +                mips32_op = OPC_TLTI; +                goto do_trapi; +            } +            break; +        case TGEI: /* BC1NEZC */ +            if (ctx->insn_flags & ISA_MIPS32R6) { +                /* BC1NEZC */ +                check_cp1_enabled(ctx); +                gen_compute_branch1_r6(ctx, OPC_BC1NEZ, rs, imm << 1, 0); +            } else { +                /* TGEI */ +                mips32_op = OPC_TGEI; +                goto do_trapi; +            } +            break; +        case TLTIU: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_TLTIU; +            goto do_trapi; +        case TGEIU: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_TGEIU; +            goto do_trapi; +        case TNEI: /* SYNCI */ +            if (ctx->insn_flags & ISA_MIPS32R6) { +                /* SYNCI */ +                /* Break the TB to be able to sync copied instructions +                   immediately */ +                ctx->bstate = BS_STOP; +            } else { +                /* TNEI */ +                mips32_op = OPC_TNEI; +                goto do_trapi; +            } +            break; +        case TEQI: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_TEQI; +        do_trapi: +            gen_trap(ctx, mips32_op, rs, -1, imm); +            break; + +        case BNEZC: +        case BEQZC: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch(ctx, minor == BNEZC ? OPC_BNE : OPC_BEQ, +                               4, rs, 0, imm << 1, 0); +            /* Compact branches don't have a delay slot, so just let +               the normal delay slot handling take us to the branch +               target. */ +            break; +        case LUI: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_logic_imm(ctx, OPC_LUI, rs, 0, imm); +            break; +        case SYNCI: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            /* Break the TB to be able to sync copied instructions +               immediately */ +            ctx->bstate = BS_STOP; +            break; +        case BC2F: +        case BC2T: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            /* COP2: Not implemented. */ +            generate_exception_err(ctx, EXCP_CpU, 2); +            break; +        case BC1F: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = (ctx->opcode & (1 << 16)) ? OPC_BC1FANY2 : OPC_BC1F; +            goto do_cp1branch; +        case BC1T: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = (ctx->opcode & (1 << 16)) ? OPC_BC1TANY2 : OPC_BC1T; +            goto do_cp1branch; +        case BC1ANY4F: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_BC1FANY4; +            goto do_cp1mips3d; +        case BC1ANY4T: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_BC1TANY4; +        do_cp1mips3d: +            check_cop1x(ctx); +            check_insn(ctx, ASE_MIPS3D); +            /* Fall through */ +        do_cp1branch: +            if (env->CP0_Config1 & (1 << CP0C1_FP)) { +                check_cp1_enabled(ctx); +                gen_compute_branch1(ctx, mips32_op, +                                    (ctx->opcode >> 18) & 0x7, imm << 1); +            } else { +                generate_exception_err(ctx, EXCP_CpU, 1); +            } +            break; +        case BPOSGE64: +        case BPOSGE32: +            /* MIPS DSP: not implemented */ +            /* Fall through */ +        default: +            MIPS_INVAL("pool32i"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case POOL32C: +        minor = (ctx->opcode >> 12) & 0xf; +        offset = sextract32(ctx->opcode, 0, +                            (ctx->insn_flags & ISA_MIPS32R6) ? 9 : 12); +        switch (minor) { +        case LWL: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_LWL; +            goto do_ld_lr; +        case SWL: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_SWL; +            goto do_st_lr; +        case LWR: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_LWR; +            goto do_ld_lr; +        case SWR: +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_SWR; +            goto do_st_lr; +#if defined(TARGET_MIPS64) +        case LDL: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_LDL; +            goto do_ld_lr; +        case SDL: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_SDL; +            goto do_st_lr; +        case LDR: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_LDR; +            goto do_ld_lr; +        case SDR: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            mips32_op = OPC_SDR; +            goto do_st_lr; +        case LWU: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            mips32_op = OPC_LWU; +            goto do_ld_lr; +        case LLD: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            mips32_op = OPC_LLD; +            goto do_ld_lr; +#endif +        case LL: +            mips32_op = OPC_LL; +            goto do_ld_lr; +        do_ld_lr: +            gen_ld(ctx, mips32_op, rt, rs, offset); +            break; +        do_st_lr: +            gen_st(ctx, mips32_op, rt, rs, SIMM(ctx->opcode, 0, 12)); +            break; +        case SC: +            gen_st_cond(ctx, OPC_SC, rt, rs, offset); +            break; +#if defined(TARGET_MIPS64) +        case SCD: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_st_cond(ctx, OPC_SCD, rt, rs, offset); +            break; +#endif +        case PREF: +            /* Treat as no-op */ +            if ((ctx->insn_flags & ISA_MIPS32R6) && (rt >= 24)) { +                /* hint codes 24-31 are reserved and signal RI */ +                generate_exception(ctx, EXCP_RI); +            } +            break; +        default: +            MIPS_INVAL("pool32c"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case ADDI32: /* AUI, LUI */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* AUI, LUI */ +            gen_logic_imm(ctx, OPC_LUI, rt, rs, imm); +        } else { +            /* ADDI32 */ +            mips32_op = OPC_ADDI; +            goto do_addi; +        } +        break; +    case ADDIU32: +        mips32_op = OPC_ADDIU; +    do_addi: +        gen_arith_imm(ctx, mips32_op, rt, rs, imm); +        break; + +        /* Logical operations */ +    case ORI32: +        mips32_op = OPC_ORI; +        goto do_logici; +    case XORI32: +        mips32_op = OPC_XORI; +        goto do_logici; +    case ANDI32: +        mips32_op = OPC_ANDI; +    do_logici: +        gen_logic_imm(ctx, mips32_op, rt, rs, imm); +        break; + +        /* Set less than immediate */ +    case SLTI32: +        mips32_op = OPC_SLTI; +        goto do_slti; +    case SLTIU32: +        mips32_op = OPC_SLTIU; +    do_slti: +        gen_slt_imm(ctx, mips32_op, rt, rs, imm); +        break; +    case JALX32: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; +        gen_compute_branch(ctx, OPC_JALX, 4, rt, rs, offset, 4); +        ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +        break; +    case JALS32: /* BOVC, BEQC, BEQZALC */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            if (rs >= rt) { +                /* BOVC */ +                mips32_op = OPC_BOVC; +            } else if (rs < rt && rs == 0) { +                /* BEQZALC */ +                mips32_op = OPC_BEQZALC; +            } else { +                /* BEQC */ +                mips32_op = OPC_BEQC; +            } +            gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); +        } else { +            /* JALS32 */ +            offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 1; +            gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, offset, 2); +            ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +        } +        break; +    case BEQ32: /* BC */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* BC */ +            gen_compute_compact_branch(ctx, OPC_BC, 0, 0, +                                       sextract32(ctx->opcode << 1, 0, 27)); +        } else { +            /* BEQ32 */ +            gen_compute_branch(ctx, OPC_BEQ, 4, rt, rs, imm << 1, 4); +        } +        break; +    case BNE32: /* BALC */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* BALC */ +            gen_compute_compact_branch(ctx, OPC_BALC, 0, 0, +                                       sextract32(ctx->opcode << 1, 0, 27)); +        } else { +            /* BNE32 */ +            gen_compute_branch(ctx, OPC_BNE, 4, rt, rs, imm << 1, 4); +        } +        break; +    case J32: /* BGTZC, BLTZC, BLTC */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            if (rs == 0 && rt != 0) { +                /* BGTZC */ +                mips32_op = OPC_BGTZC; +            } else if (rs != 0 && rt != 0 && rs == rt) { +                /* BLTZC */ +                mips32_op = OPC_BLTZC; +            } else { +                /* BLTC */ +                mips32_op = OPC_BLTC; +            } +            gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); +        } else { +            /* J32 */ +            gen_compute_branch(ctx, OPC_J, 4, rt, rs, +                               (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4); +        } +        break; +    case JAL32: /* BLEZC, BGEZC, BGEC */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            if (rs == 0 && rt != 0) { +                /* BLEZC */ +                mips32_op = OPC_BLEZC; +            } else if (rs != 0 && rt != 0 && rs == rt) { +                /* BGEZC */ +                mips32_op = OPC_BGEZC; +            } else { +                /* BGEC */ +                mips32_op = OPC_BGEC; +            } +            gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); +        } else { +            /* JAL32 */ +            gen_compute_branch(ctx, OPC_JAL, 4, rt, rs, +                               (int32_t)(ctx->opcode & 0x3FFFFFF) << 1, 4); +            ctx->hflags |= MIPS_HFLAG_BDS_STRICT; +        } +        break; +        /* Floating point (COP1) */ +    case LWC132: +        mips32_op = OPC_LWC1; +        goto do_cop1; +    case LDC132: +        mips32_op = OPC_LDC1; +        goto do_cop1; +    case SWC132: +        mips32_op = OPC_SWC1; +        goto do_cop1; +    case SDC132: +        mips32_op = OPC_SDC1; +    do_cop1: +        gen_cop1_ldst(ctx, mips32_op, rt, rs, imm); +        break; +    case ADDIUPC: /* PCREL: ADDIUPC, AUIPC, ALUIPC, LWPC */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* PCREL: ADDIUPC, AUIPC, ALUIPC, LWPC */ +            switch ((ctx->opcode >> 16) & 0x1f) { +            case ADDIUPC_00 ... ADDIUPC_07: +                gen_pcrel(ctx, OPC_ADDIUPC, ctx->pc & ~0x3, rt); +                break; +            case AUIPC: +                gen_pcrel(ctx, OPC_AUIPC, ctx->pc, rt); +                break; +            case ALUIPC: +                gen_pcrel(ctx, OPC_ALUIPC, ctx->pc, rt); +                break; +            case LWPC_08 ... LWPC_0F: +                gen_pcrel(ctx, R6_OPC_LWPC, ctx->pc & ~0x3, rt); +                break; +            default: +                generate_exception(ctx, EXCP_RI); +                break; +            } +        } else { +            /* ADDIUPC */ +            int reg = mmreg(ZIMM(ctx->opcode, 23, 3)); +            int offset = SIMM(ctx->opcode, 0, 23) << 2; + +            gen_addiupc(ctx, reg, offset, 0, 0); +        } +        break; +    case BNVC: /* BNEC, BNEZALC */ +        check_insn(ctx, ISA_MIPS32R6); +        if (rs >= rt) { +            /* BNVC */ +            mips32_op = OPC_BNVC; +        } else if (rs < rt && rs == 0) { +            /* BNEZALC */ +            mips32_op = OPC_BNEZALC; +        } else { +            /* BNEC */ +            mips32_op = OPC_BNEC; +        } +        gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); +        break; +    case R6_BNEZC: /* JIALC */ +        check_insn(ctx, ISA_MIPS32R6); +        if (rt != 0) { +            /* BNEZC */ +            gen_compute_compact_branch(ctx, OPC_BNEZC, rt, 0, +                                       sextract32(ctx->opcode << 1, 0, 22)); +        } else { +            /* JIALC */ +            gen_compute_compact_branch(ctx, OPC_JIALC, 0, rs, imm); +        } +        break; +    case R6_BEQZC: /* JIC */ +        check_insn(ctx, ISA_MIPS32R6); +        if (rt != 0) { +            /* BEQZC */ +            gen_compute_compact_branch(ctx, OPC_BEQZC, rt, 0, +                                       sextract32(ctx->opcode << 1, 0, 22)); +        } else { +            /* JIC */ +            gen_compute_compact_branch(ctx, OPC_JIC, 0, rs, imm); +        } +        break; +    case BLEZALC: /* BGEZALC, BGEUC */ +        check_insn(ctx, ISA_MIPS32R6); +        if (rs == 0 && rt != 0) { +            /* BLEZALC */ +            mips32_op = OPC_BLEZALC; +        } else if (rs != 0 && rt != 0 && rs == rt) { +            /* BGEZALC */ +            mips32_op = OPC_BGEZALC; +        } else { +            /* BGEUC */ +            mips32_op = OPC_BGEUC; +        } +        gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); +        break; +    case BGTZALC: /* BLTZALC, BLTUC */ +        check_insn(ctx, ISA_MIPS32R6); +        if (rs == 0 && rt != 0) { +            /* BGTZALC */ +            mips32_op = OPC_BGTZALC; +        } else if (rs != 0 && rt != 0 && rs == rt) { +            /* BLTZALC */ +            mips32_op = OPC_BLTZALC; +        } else { +            /* BLTUC */ +            mips32_op = OPC_BLTUC; +        } +        gen_compute_compact_branch(ctx, mips32_op, rs, rt, imm << 1); +        break; +        /* Loads and stores */ +    case LB32: +        mips32_op = OPC_LB; +        goto do_ld; +    case LBU32: +        mips32_op = OPC_LBU; +        goto do_ld; +    case LH32: +        mips32_op = OPC_LH; +        goto do_ld; +    case LHU32: +        mips32_op = OPC_LHU; +        goto do_ld; +    case LW32: +        mips32_op = OPC_LW; +        goto do_ld; +#ifdef TARGET_MIPS64 +    case LD32: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        mips32_op = OPC_LD; +        goto do_ld; +    case SD32: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        mips32_op = OPC_SD; +        goto do_st; +#endif +    case SB32: +        mips32_op = OPC_SB; +        goto do_st; +    case SH32: +        mips32_op = OPC_SH; +        goto do_st; +    case SW32: +        mips32_op = OPC_SW; +        goto do_st; +    do_ld: +        gen_ld(ctx, mips32_op, rt, rs, imm); +        break; +    do_st: +        gen_st(ctx, mips32_op, rt, rs, imm); +        break; +    default: +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) +{ +    uint32_t op; + +    /* make sure instructions are on a halfword boundary */ +    if (ctx->pc & 0x1) { +        env->CP0_BadVAddr = ctx->pc; +        generate_exception(ctx, EXCP_AdEL); +        ctx->bstate = BS_STOP; +        return 2; +    } + +    op = (ctx->opcode >> 10) & 0x3f; +    /* Enforce properly-sized instructions in a delay slot */ +    if (ctx->hflags & MIPS_HFLAG_BDS_STRICT) { +        switch (op & 0x7) { /* MSB-3..MSB-5 */ +        case 0: +        /* POOL32A, POOL32B, POOL32I, POOL32C */ +        case 4: +        /* ADDI32, ADDIU32, ORI32, XORI32, SLTI32, SLTIU32, ANDI32, JALX32 */ +        case 5: +        /* LBU32, LHU32, POOL32F, JALS32, BEQ32, BNE32, J32, JAL32 */ +        case 6: +        /* SB32, SH32, ADDIUPC, SWC132, SDC132, SW32 */ +        case 7: +        /* LB32, LH32, LWC132, LDC132, LW32 */ +            if (ctx->hflags & MIPS_HFLAG_BDS16) { +                generate_exception(ctx, EXCP_RI); +                /* Just stop translation; the user is confused.  */ +                ctx->bstate = BS_STOP; +                return 2; +            } +            break; +        case 1: +        /* POOL16A, POOL16B, POOL16C, LWGP16, POOL16F */ +        case 2: +        /* LBU16, LHU16, LWSP16, LW16, SB16, SH16, SWSP16, SW16 */ +        case 3: +        /* MOVE16, ANDI16, POOL16D, POOL16E, BEQZ16, BNEZ16, B16, LI16 */ +            if (ctx->hflags & MIPS_HFLAG_BDS32) { +                generate_exception(ctx, EXCP_RI); +                /* Just stop translation; the user is confused.  */ +                ctx->bstate = BS_STOP; +                return 2; +            } +            break; +        } +    } + +    switch (op) { +    case POOL16A: +        { +            int rd = mmreg(uMIPS_RD(ctx->opcode)); +            int rs1 = mmreg(uMIPS_RS1(ctx->opcode)); +            int rs2 = mmreg(uMIPS_RS2(ctx->opcode)); +            uint32_t opc = 0; + +            switch (ctx->opcode & 0x1) { +            case ADDU16: +                opc = OPC_ADDU; +                break; +            case SUBU16: +                opc = OPC_SUBU; +                break; +            } +            if (ctx->insn_flags & ISA_MIPS32R6) { +                /* In the Release 6 the register number location in +                 * the instruction encoding has changed. +                 */ +                gen_arith(ctx, opc, rs1, rd, rs2); +            } else { +                gen_arith(ctx, opc, rd, rs1, rs2); +            } +        } +        break; +    case POOL16B: +        { +            int rd = mmreg(uMIPS_RD(ctx->opcode)); +            int rs = mmreg(uMIPS_RS(ctx->opcode)); +            int amount = (ctx->opcode >> 1) & 0x7; +            uint32_t opc = 0; +            amount = amount == 0 ? 8 : amount; + +            switch (ctx->opcode & 0x1) { +            case SLL16: +                opc = OPC_SLL; +                break; +            case SRL16: +                opc = OPC_SRL; +                break; +            } + +            gen_shift_imm(ctx, opc, rd, rs, amount); +        } +        break; +    case POOL16C: +        if (ctx->insn_flags & ISA_MIPS32R6) { +            gen_pool16c_r6_insn(ctx); +        } else { +            gen_pool16c_insn(ctx); +        } +        break; +    case LWGP16: +        { +            int rd = mmreg(uMIPS_RD(ctx->opcode)); +            int rb = 28;            /* GP */ +            int16_t offset = SIMM(ctx->opcode, 0, 7) << 2; + +            gen_ld(ctx, OPC_LW, rd, rb, offset); +        } +        break; +    case POOL16F: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        if (ctx->opcode & 1) { +            generate_exception(ctx, EXCP_RI); +        } else { +            /* MOVEP */ +            int enc_dest = uMIPS_RD(ctx->opcode); +            int enc_rt = uMIPS_RS2(ctx->opcode); +            int enc_rs = uMIPS_RS1(ctx->opcode); +            gen_movep(ctx, enc_dest, enc_rt, enc_rs); +        } +        break; +    case LBU16: +        { +            int rd = mmreg(uMIPS_RD(ctx->opcode)); +            int rb = mmreg(uMIPS_RS(ctx->opcode)); +            int16_t offset = ZIMM(ctx->opcode, 0, 4); +            offset = (offset == 0xf ? -1 : offset); + +            gen_ld(ctx, OPC_LBU, rd, rb, offset); +        } +        break; +    case LHU16: +        { +            int rd = mmreg(uMIPS_RD(ctx->opcode)); +            int rb = mmreg(uMIPS_RS(ctx->opcode)); +            int16_t offset = ZIMM(ctx->opcode, 0, 4) << 1; + +            gen_ld(ctx, OPC_LHU, rd, rb, offset); +        } +        break; +    case LWSP16: +        { +            int rd = (ctx->opcode >> 5) & 0x1f; +            int rb = 29;            /* SP */ +            int16_t offset = ZIMM(ctx->opcode, 0, 5) << 2; + +            gen_ld(ctx, OPC_LW, rd, rb, offset); +        } +        break; +    case LW16: +        { +            int rd = mmreg(uMIPS_RD(ctx->opcode)); +            int rb = mmreg(uMIPS_RS(ctx->opcode)); +            int16_t offset = ZIMM(ctx->opcode, 0, 4) << 2; + +            gen_ld(ctx, OPC_LW, rd, rb, offset); +        } +        break; +    case SB16: +        { +            int rd = mmreg2(uMIPS_RD(ctx->opcode)); +            int rb = mmreg(uMIPS_RS(ctx->opcode)); +            int16_t offset = ZIMM(ctx->opcode, 0, 4); + +            gen_st(ctx, OPC_SB, rd, rb, offset); +        } +        break; +    case SH16: +        { +            int rd = mmreg2(uMIPS_RD(ctx->opcode)); +            int rb = mmreg(uMIPS_RS(ctx->opcode)); +            int16_t offset = ZIMM(ctx->opcode, 0, 4) << 1; + +            gen_st(ctx, OPC_SH, rd, rb, offset); +        } +        break; +    case SWSP16: +        { +            int rd = (ctx->opcode >> 5) & 0x1f; +            int rb = 29;            /* SP */ +            int16_t offset = ZIMM(ctx->opcode, 0, 5) << 2; + +            gen_st(ctx, OPC_SW, rd, rb, offset); +        } +        break; +    case SW16: +        { +            int rd = mmreg2(uMIPS_RD(ctx->opcode)); +            int rb = mmreg(uMIPS_RS(ctx->opcode)); +            int16_t offset = ZIMM(ctx->opcode, 0, 4) << 2; + +            gen_st(ctx, OPC_SW, rd, rb, offset); +        } +        break; +    case MOVE16: +        { +            int rd = uMIPS_RD5(ctx->opcode); +            int rs = uMIPS_RS5(ctx->opcode); + +            gen_arith(ctx, OPC_ADDU, rd, rs, 0); +        } +        break; +    case ANDI16: +        gen_andi16(ctx); +        break; +    case POOL16D: +        switch (ctx->opcode & 0x1) { +        case ADDIUS5: +            gen_addius5(ctx); +            break; +        case ADDIUSP: +            gen_addiusp(ctx); +            break; +        } +        break; +    case POOL16E: +        switch (ctx->opcode & 0x1) { +        case ADDIUR2: +            gen_addiur2(ctx); +            break; +        case ADDIUR1SP: +            gen_addiur1sp(ctx); +            break; +        } +        break; +    case B16: /* BC16 */ +        gen_compute_branch(ctx, OPC_BEQ, 2, 0, 0, +                           sextract32(ctx->opcode, 0, 10) << 1, +                           (ctx->insn_flags & ISA_MIPS32R6) ? 0 : 4); +        break; +    case BNEZ16: /* BNEZC16 */ +    case BEQZ16: /* BEQZC16 */ +        gen_compute_branch(ctx, op == BNEZ16 ? OPC_BNE : OPC_BEQ, 2, +                           mmreg(uMIPS_RD(ctx->opcode)), +                           0, sextract32(ctx->opcode, 0, 7) << 1, +                           (ctx->insn_flags & ISA_MIPS32R6) ? 0 : 4); + +        break; +    case LI16: +        { +            int reg = mmreg(uMIPS_RD(ctx->opcode)); +            int imm = ZIMM(ctx->opcode, 0, 7); + +            imm = (imm == 0x7f ? -1 : imm); +            tcg_gen_movi_tl(cpu_gpr[reg], imm); +        } +        break; +    case RES_29: +    case RES_31: +    case RES_39: +        generate_exception(ctx, EXCP_RI); +        break; +    default: +        decode_micromips32_opc(env, ctx); +        return 4; +    } + +    return 2; +} + +/* SmartMIPS extension to MIPS32 */ + +#if defined(TARGET_MIPS64) + +/* MDMX extension to MIPS64 */ + +#endif + +/* MIPSDSP functions. */ +static void gen_mipsdsp_ld(DisasContext *ctx, uint32_t opc, +                           int rd, int base, int offset) +{ +    const char *opn = "ldx"; +    TCGv t0; + +    check_dsp(ctx); +    t0 = tcg_temp_new(); + +    if (base == 0) { +        gen_load_gpr(t0, offset); +    } else if (offset == 0) { +        gen_load_gpr(t0, base); +    } else { +        gen_op_addr_add(ctx, t0, cpu_gpr[base], cpu_gpr[offset]); +    } + +    switch (opc) { +    case OPC_LBUX: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_UB); +        gen_store_gpr(t0, rd); +        opn = "lbux"; +        break; +    case OPC_LHX: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW); +        gen_store_gpr(t0, rd); +        opn = "lhx"; +        break; +    case OPC_LWX: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESL); +        gen_store_gpr(t0, rd); +        opn = "lwx"; +        break; +#if defined(TARGET_MIPS64) +    case OPC_LDX: +        tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TEQ); +        gen_store_gpr(t0, rd); +        opn = "ldx"; +        break; +#endif +    } +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s %s, %s(%s)", opn, +               regnames[rd], regnames[offset], regnames[base]); +    tcg_temp_free(t0); +} + +static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, +                              int ret, int v1, int v2) +{ +    const char *opn = "mipsdsp arith"; +    TCGv v1_t; +    TCGv v2_t; + +    if (ret == 0) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    v1_t = tcg_temp_new(); +    v2_t = tcg_temp_new(); + +    gen_load_gpr(v1_t, v1); +    gen_load_gpr(v2_t, v2); + +    switch (op1) { +    /* OPC_MULT_G_2E is equal OPC_ADDUH_QB_DSP */ +    case OPC_MULT_G_2E: +        check_dspr2(ctx); +        switch (op2) { +        case OPC_ADDUH_QB: +            gen_helper_adduh_qb(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_ADDUH_R_QB: +            gen_helper_adduh_r_qb(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_ADDQH_PH: +            gen_helper_addqh_ph(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_ADDQH_R_PH: +            gen_helper_addqh_r_ph(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_ADDQH_W: +            gen_helper_addqh_w(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_ADDQH_R_W: +            gen_helper_addqh_r_w(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_SUBUH_QB: +            gen_helper_subuh_qb(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_SUBUH_R_QB: +            gen_helper_subuh_r_qb(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_SUBQH_PH: +            gen_helper_subqh_ph(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_SUBQH_R_PH: +            gen_helper_subqh_r_ph(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_SUBQH_W: +            gen_helper_subqh_w(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_SUBQH_R_W: +            gen_helper_subqh_r_w(cpu_gpr[ret], v1_t, v2_t); +            break; +        } +        break; +    case OPC_ABSQ_S_PH_DSP: +        switch (op2) { +        case OPC_ABSQ_S_QB: +            check_dspr2(ctx); +            gen_helper_absq_s_qb(cpu_gpr[ret], v2_t, cpu_env); +            break; +        case OPC_ABSQ_S_PH: +            check_dsp(ctx); +            gen_helper_absq_s_ph(cpu_gpr[ret], v2_t, cpu_env); +            break; +        case OPC_ABSQ_S_W: +            check_dsp(ctx); +            gen_helper_absq_s_w(cpu_gpr[ret], v2_t, cpu_env); +            break; +        case OPC_PRECEQ_W_PHL: +            check_dsp(ctx); +            tcg_gen_andi_tl(cpu_gpr[ret], v2_t, 0xFFFF0000); +            tcg_gen_ext32s_tl(cpu_gpr[ret], cpu_gpr[ret]); +            break; +        case OPC_PRECEQ_W_PHR: +            check_dsp(ctx); +            tcg_gen_andi_tl(cpu_gpr[ret], v2_t, 0x0000FFFF); +            tcg_gen_shli_tl(cpu_gpr[ret], cpu_gpr[ret], 16); +            tcg_gen_ext32s_tl(cpu_gpr[ret], cpu_gpr[ret]); +            break; +        case OPC_PRECEQU_PH_QBL: +            check_dsp(ctx); +            gen_helper_precequ_ph_qbl(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEQU_PH_QBR: +            check_dsp(ctx); +            gen_helper_precequ_ph_qbr(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEQU_PH_QBLA: +            check_dsp(ctx); +            gen_helper_precequ_ph_qbla(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEQU_PH_QBRA: +            check_dsp(ctx); +            gen_helper_precequ_ph_qbra(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEU_PH_QBL: +            check_dsp(ctx); +            gen_helper_preceu_ph_qbl(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEU_PH_QBR: +            check_dsp(ctx); +            gen_helper_preceu_ph_qbr(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEU_PH_QBLA: +            check_dsp(ctx); +            gen_helper_preceu_ph_qbla(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEU_PH_QBRA: +            check_dsp(ctx); +            gen_helper_preceu_ph_qbra(cpu_gpr[ret], v2_t); +            break; +        } +        break; +    case OPC_ADDU_QB_DSP: +        switch (op2) { +        case OPC_ADDQ_PH: +            check_dsp(ctx); +            gen_helper_addq_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDQ_S_PH: +            check_dsp(ctx); +            gen_helper_addq_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDQ_S_W: +            check_dsp(ctx); +            gen_helper_addq_s_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDU_QB: +            check_dsp(ctx); +            gen_helper_addu_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDU_S_QB: +            check_dsp(ctx); +            gen_helper_addu_s_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDU_PH: +            check_dspr2(ctx); +            gen_helper_addu_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDU_S_PH: +            check_dspr2(ctx); +            gen_helper_addu_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBQ_PH: +            check_dsp(ctx); +            gen_helper_subq_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBQ_S_PH: +            check_dsp(ctx); +            gen_helper_subq_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBQ_S_W: +            check_dsp(ctx); +            gen_helper_subq_s_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBU_QB: +            check_dsp(ctx); +            gen_helper_subu_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBU_S_QB: +            check_dsp(ctx); +            gen_helper_subu_s_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBU_PH: +            check_dspr2(ctx); +            gen_helper_subu_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBU_S_PH: +            check_dspr2(ctx); +            gen_helper_subu_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDSC: +            check_dsp(ctx); +            gen_helper_addsc(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDWC: +            check_dsp(ctx); +            gen_helper_addwc(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MODSUB: +            check_dsp(ctx); +            gen_helper_modsub(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_RADDU_W_QB: +            check_dsp(ctx); +            gen_helper_raddu_w_qb(cpu_gpr[ret], v1_t); +            break; +        } +        break; +    case OPC_CMPU_EQ_QB_DSP: +        switch (op2) { +        case OPC_PRECR_QB_PH: +            check_dspr2(ctx); +            gen_helper_precr_qb_ph(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_PRECRQ_QB_PH: +            check_dsp(ctx); +            gen_helper_precrq_qb_ph(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_PRECR_SRA_PH_W: +            check_dspr2(ctx); +            { +                TCGv_i32 sa_t = tcg_const_i32(v2); +                gen_helper_precr_sra_ph_w(cpu_gpr[ret], sa_t, v1_t, +                                          cpu_gpr[ret]); +                tcg_temp_free_i32(sa_t); +                break; +            } +        case OPC_PRECR_SRA_R_PH_W: +            check_dspr2(ctx); +            { +                TCGv_i32 sa_t = tcg_const_i32(v2); +                gen_helper_precr_sra_r_ph_w(cpu_gpr[ret], sa_t, v1_t, +                                            cpu_gpr[ret]); +                tcg_temp_free_i32(sa_t); +                break; +            } +        case OPC_PRECRQ_PH_W: +            check_dsp(ctx); +            gen_helper_precrq_ph_w(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_PRECRQ_RS_PH_W: +            check_dsp(ctx); +            gen_helper_precrq_rs_ph_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_PRECRQU_S_QB_PH: +            check_dsp(ctx); +            gen_helper_precrqu_s_qb_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        } +        break; +#ifdef TARGET_MIPS64 +    case OPC_ABSQ_S_QH_DSP: +        switch (op2) { +        case OPC_PRECEQ_L_PWL: +            check_dsp(ctx); +            tcg_gen_andi_tl(cpu_gpr[ret], v2_t, 0xFFFFFFFF00000000ull); +            break; +        case OPC_PRECEQ_L_PWR: +            check_dsp(ctx); +            tcg_gen_shli_tl(cpu_gpr[ret], v2_t, 32); +            break; +        case OPC_PRECEQ_PW_QHL: +            check_dsp(ctx); +            gen_helper_preceq_pw_qhl(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEQ_PW_QHR: +            check_dsp(ctx); +            gen_helper_preceq_pw_qhr(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEQ_PW_QHLA: +            check_dsp(ctx); +            gen_helper_preceq_pw_qhla(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEQ_PW_QHRA: +            check_dsp(ctx); +            gen_helper_preceq_pw_qhra(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEQU_QH_OBL: +            check_dsp(ctx); +            gen_helper_precequ_qh_obl(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEQU_QH_OBR: +            check_dsp(ctx); +            gen_helper_precequ_qh_obr(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEQU_QH_OBLA: +            check_dsp(ctx); +            gen_helper_precequ_qh_obla(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEQU_QH_OBRA: +            check_dsp(ctx); +            gen_helper_precequ_qh_obra(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEU_QH_OBL: +            check_dsp(ctx); +            gen_helper_preceu_qh_obl(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEU_QH_OBR: +            check_dsp(ctx); +            gen_helper_preceu_qh_obr(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEU_QH_OBLA: +            check_dsp(ctx); +            gen_helper_preceu_qh_obla(cpu_gpr[ret], v2_t); +            break; +        case OPC_PRECEU_QH_OBRA: +            check_dsp(ctx); +            gen_helper_preceu_qh_obra(cpu_gpr[ret], v2_t); +            break; +        case OPC_ABSQ_S_OB: +            check_dspr2(ctx); +            gen_helper_absq_s_ob(cpu_gpr[ret], v2_t, cpu_env); +            break; +        case OPC_ABSQ_S_PW: +            check_dsp(ctx); +            gen_helper_absq_s_pw(cpu_gpr[ret], v2_t, cpu_env); +            break; +        case OPC_ABSQ_S_QH: +            check_dsp(ctx); +            gen_helper_absq_s_qh(cpu_gpr[ret], v2_t, cpu_env); +            break; +        } +        break; +    case OPC_ADDU_OB_DSP: +        switch (op2) { +        case OPC_RADDU_L_OB: +            check_dsp(ctx); +            gen_helper_raddu_l_ob(cpu_gpr[ret], v1_t); +            break; +        case OPC_SUBQ_PW: +            check_dsp(ctx); +            gen_helper_subq_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBQ_S_PW: +            check_dsp(ctx); +            gen_helper_subq_s_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBQ_QH: +            check_dsp(ctx); +            gen_helper_subq_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBQ_S_QH: +            check_dsp(ctx); +            gen_helper_subq_s_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBU_OB: +            check_dsp(ctx); +            gen_helper_subu_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBU_S_OB: +            check_dsp(ctx); +            gen_helper_subu_s_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBU_QH: +            check_dspr2(ctx); +            gen_helper_subu_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBU_S_QH: +            check_dspr2(ctx); +            gen_helper_subu_s_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_SUBUH_OB: +            check_dspr2(ctx); +            gen_helper_subuh_ob(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_SUBUH_R_OB: +            check_dspr2(ctx); +            gen_helper_subuh_r_ob(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_ADDQ_PW: +            check_dsp(ctx); +            gen_helper_addq_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDQ_S_PW: +            check_dsp(ctx); +            gen_helper_addq_s_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDQ_QH: +            check_dsp(ctx); +            gen_helper_addq_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDQ_S_QH: +            check_dsp(ctx); +            gen_helper_addq_s_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDU_OB: +            check_dsp(ctx); +            gen_helper_addu_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDU_S_OB: +            check_dsp(ctx); +            gen_helper_addu_s_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDU_QH: +            check_dspr2(ctx); +            gen_helper_addu_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDU_S_QH: +            check_dspr2(ctx); +            gen_helper_addu_s_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_ADDUH_OB: +            check_dspr2(ctx); +            gen_helper_adduh_ob(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_ADDUH_R_OB: +            check_dspr2(ctx); +            gen_helper_adduh_r_ob(cpu_gpr[ret], v1_t, v2_t); +            break; +        } +        break; +    case OPC_CMPU_EQ_OB_DSP: +        switch (op2) { +        case OPC_PRECR_OB_QH: +            check_dspr2(ctx); +            gen_helper_precr_ob_qh(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_PRECR_SRA_QH_PW: +            check_dspr2(ctx); +            { +                TCGv_i32 ret_t = tcg_const_i32(ret); +                gen_helper_precr_sra_qh_pw(v2_t, v1_t, v2_t, ret_t); +                tcg_temp_free_i32(ret_t); +                break; +            } +        case OPC_PRECR_SRA_R_QH_PW: +            check_dspr2(ctx); +            { +                TCGv_i32 sa_v = tcg_const_i32(ret); +                gen_helper_precr_sra_r_qh_pw(v2_t, v1_t, v2_t, sa_v); +                tcg_temp_free_i32(sa_v); +                break; +            } +        case OPC_PRECRQ_OB_QH: +            check_dsp(ctx); +            gen_helper_precrq_ob_qh(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_PRECRQ_PW_L: +            check_dsp(ctx); +            gen_helper_precrq_pw_l(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_PRECRQ_QH_PW: +            check_dsp(ctx); +            gen_helper_precrq_qh_pw(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_PRECRQ_RS_QH_PW: +            check_dsp(ctx); +            gen_helper_precrq_rs_qh_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_PRECRQU_S_OB_QH: +            check_dsp(ctx); +            gen_helper_precrqu_s_ob_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        } +        break; +#endif +    } + +    tcg_temp_free(v1_t); +    tcg_temp_free(v2_t); + +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s", opn); +} + +static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, +                              int ret, int v1, int v2) +{ +    uint32_t op2; +    const char *opn = "mipsdsp shift"; +    TCGv t0; +    TCGv v1_t; +    TCGv v2_t; + +    if (ret == 0) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    v1_t = tcg_temp_new(); +    v2_t = tcg_temp_new(); + +    tcg_gen_movi_tl(t0, v1); +    gen_load_gpr(v1_t, v1); +    gen_load_gpr(v2_t, v2); + +    switch (opc) { +    case OPC_SHLL_QB_DSP: +        { +            op2 = MASK_SHLL_QB(ctx->opcode); +            switch (op2) { +            case OPC_SHLL_QB: +                check_dsp(ctx); +                gen_helper_shll_qb(cpu_gpr[ret], t0, v2_t, cpu_env); +                break; +            case OPC_SHLLV_QB: +                check_dsp(ctx); +                gen_helper_shll_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); +                break; +            case OPC_SHLL_PH: +                check_dsp(ctx); +                gen_helper_shll_ph(cpu_gpr[ret], t0, v2_t, cpu_env); +                break; +            case OPC_SHLLV_PH: +                check_dsp(ctx); +                gen_helper_shll_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +                break; +            case OPC_SHLL_S_PH: +                check_dsp(ctx); +                gen_helper_shll_s_ph(cpu_gpr[ret], t0, v2_t, cpu_env); +                break; +            case OPC_SHLLV_S_PH: +                check_dsp(ctx); +                gen_helper_shll_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +                break; +            case OPC_SHLL_S_W: +                check_dsp(ctx); +                gen_helper_shll_s_w(cpu_gpr[ret], t0, v2_t, cpu_env); +                break; +            case OPC_SHLLV_S_W: +                check_dsp(ctx); +                gen_helper_shll_s_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); +                break; +            case OPC_SHRL_QB: +                check_dsp(ctx); +                gen_helper_shrl_qb(cpu_gpr[ret], t0, v2_t); +                break; +            case OPC_SHRLV_QB: +                check_dsp(ctx); +                gen_helper_shrl_qb(cpu_gpr[ret], v1_t, v2_t); +                break; +            case OPC_SHRL_PH: +                check_dspr2(ctx); +                gen_helper_shrl_ph(cpu_gpr[ret], t0, v2_t); +                break; +            case OPC_SHRLV_PH: +                check_dspr2(ctx); +                gen_helper_shrl_ph(cpu_gpr[ret], v1_t, v2_t); +                break; +            case OPC_SHRA_QB: +                check_dspr2(ctx); +                gen_helper_shra_qb(cpu_gpr[ret], t0, v2_t); +                break; +            case OPC_SHRA_R_QB: +                check_dspr2(ctx); +                gen_helper_shra_r_qb(cpu_gpr[ret], t0, v2_t); +                break; +            case OPC_SHRAV_QB: +                check_dspr2(ctx); +                gen_helper_shra_qb(cpu_gpr[ret], v1_t, v2_t); +                break; +            case OPC_SHRAV_R_QB: +                check_dspr2(ctx); +                gen_helper_shra_r_qb(cpu_gpr[ret], v1_t, v2_t); +                break; +            case OPC_SHRA_PH: +                check_dsp(ctx); +                gen_helper_shra_ph(cpu_gpr[ret], t0, v2_t); +                break; +            case OPC_SHRA_R_PH: +                check_dsp(ctx); +                gen_helper_shra_r_ph(cpu_gpr[ret], t0, v2_t); +                break; +            case OPC_SHRAV_PH: +                check_dsp(ctx); +                gen_helper_shra_ph(cpu_gpr[ret], v1_t, v2_t); +                break; +            case OPC_SHRAV_R_PH: +                check_dsp(ctx); +                gen_helper_shra_r_ph(cpu_gpr[ret], v1_t, v2_t); +                break; +            case OPC_SHRA_R_W: +                check_dsp(ctx); +                gen_helper_shra_r_w(cpu_gpr[ret], t0, v2_t); +                break; +            case OPC_SHRAV_R_W: +                check_dsp(ctx); +                gen_helper_shra_r_w(cpu_gpr[ret], v1_t, v2_t); +                break; +            default:            /* Invalid */ +                MIPS_INVAL("MASK SHLL.QB"); +                generate_exception(ctx, EXCP_RI); +                break; +            } +            break; +        } +#ifdef TARGET_MIPS64 +    case OPC_SHLL_OB_DSP: +        op2 = MASK_SHLL_OB(ctx->opcode); +        switch (op2) { +        case OPC_SHLL_PW: +            check_dsp(ctx); +            gen_helper_shll_pw(cpu_gpr[ret], v2_t, t0, cpu_env); +            break; +        case OPC_SHLLV_PW: +            check_dsp(ctx); +            gen_helper_shll_pw(cpu_gpr[ret], v2_t, v1_t, cpu_env); +            break; +        case OPC_SHLL_S_PW: +            check_dsp(ctx); +            gen_helper_shll_s_pw(cpu_gpr[ret], v2_t, t0, cpu_env); +            break; +        case OPC_SHLLV_S_PW: +            check_dsp(ctx); +            gen_helper_shll_s_pw(cpu_gpr[ret], v2_t, v1_t, cpu_env); +            break; +        case OPC_SHLL_OB: +            check_dsp(ctx); +            gen_helper_shll_ob(cpu_gpr[ret], v2_t, t0, cpu_env); +            break; +        case OPC_SHLLV_OB: +            check_dsp(ctx); +            gen_helper_shll_ob(cpu_gpr[ret], v2_t, v1_t, cpu_env); +            break; +        case OPC_SHLL_QH: +            check_dsp(ctx); +            gen_helper_shll_qh(cpu_gpr[ret], v2_t, t0, cpu_env); +            break; +        case OPC_SHLLV_QH: +            check_dsp(ctx); +            gen_helper_shll_qh(cpu_gpr[ret], v2_t, v1_t, cpu_env); +            break; +        case OPC_SHLL_S_QH: +            check_dsp(ctx); +            gen_helper_shll_s_qh(cpu_gpr[ret], v2_t, t0, cpu_env); +            break; +        case OPC_SHLLV_S_QH: +            check_dsp(ctx); +            gen_helper_shll_s_qh(cpu_gpr[ret], v2_t, v1_t, cpu_env); +            break; +        case OPC_SHRA_OB: +            check_dspr2(ctx); +            gen_helper_shra_ob(cpu_gpr[ret], v2_t, t0); +            break; +        case OPC_SHRAV_OB: +            check_dspr2(ctx); +            gen_helper_shra_ob(cpu_gpr[ret], v2_t, v1_t); +            break; +        case OPC_SHRA_R_OB: +            check_dspr2(ctx); +            gen_helper_shra_r_ob(cpu_gpr[ret], v2_t, t0); +            break; +        case OPC_SHRAV_R_OB: +            check_dspr2(ctx); +            gen_helper_shra_r_ob(cpu_gpr[ret], v2_t, v1_t); +            break; +        case OPC_SHRA_PW: +            check_dsp(ctx); +            gen_helper_shra_pw(cpu_gpr[ret], v2_t, t0); +            break; +        case OPC_SHRAV_PW: +            check_dsp(ctx); +            gen_helper_shra_pw(cpu_gpr[ret], v2_t, v1_t); +            break; +        case OPC_SHRA_R_PW: +            check_dsp(ctx); +            gen_helper_shra_r_pw(cpu_gpr[ret], v2_t, t0); +            break; +        case OPC_SHRAV_R_PW: +            check_dsp(ctx); +            gen_helper_shra_r_pw(cpu_gpr[ret], v2_t, v1_t); +            break; +        case OPC_SHRA_QH: +            check_dsp(ctx); +            gen_helper_shra_qh(cpu_gpr[ret], v2_t, t0); +            break; +        case OPC_SHRAV_QH: +            check_dsp(ctx); +            gen_helper_shra_qh(cpu_gpr[ret], v2_t, v1_t); +            break; +        case OPC_SHRA_R_QH: +            check_dsp(ctx); +            gen_helper_shra_r_qh(cpu_gpr[ret], v2_t, t0); +            break; +        case OPC_SHRAV_R_QH: +            check_dsp(ctx); +            gen_helper_shra_r_qh(cpu_gpr[ret], v2_t, v1_t); +            break; +        case OPC_SHRL_OB: +            check_dsp(ctx); +            gen_helper_shrl_ob(cpu_gpr[ret], v2_t, t0); +            break; +        case OPC_SHRLV_OB: +            check_dsp(ctx); +            gen_helper_shrl_ob(cpu_gpr[ret], v2_t, v1_t); +            break; +        case OPC_SHRL_QH: +            check_dspr2(ctx); +            gen_helper_shrl_qh(cpu_gpr[ret], v2_t, t0); +            break; +        case OPC_SHRLV_QH: +            check_dspr2(ctx); +            gen_helper_shrl_qh(cpu_gpr[ret], v2_t, v1_t); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK SHLL.OB"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +#endif +    } + +    tcg_temp_free(t0); +    tcg_temp_free(v1_t); +    tcg_temp_free(v2_t); +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s", opn); +} + +static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, +                                 int ret, int v1, int v2, int check_ret) +{ +    const char *opn = "mipsdsp multiply"; +    TCGv_i32 t0; +    TCGv v1_t; +    TCGv v2_t; + +    if ((ret == 0) && (check_ret == 1)) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new_i32(); +    v1_t = tcg_temp_new(); +    v2_t = tcg_temp_new(); + +    tcg_gen_movi_i32(t0, ret); +    gen_load_gpr(v1_t, v1); +    gen_load_gpr(v2_t, v2); + +    switch (op1) { +    /* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have +     * the same mask and op1. */ +    case OPC_MULT_G_2E: +        check_dspr2(ctx); +        switch (op2) { +        case  OPC_MUL_PH: +            gen_helper_mul_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case  OPC_MUL_S_PH: +            gen_helper_mul_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULQ_S_W: +            gen_helper_mulq_s_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULQ_RS_W: +            gen_helper_mulq_rs_w(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        } +        break; +    case OPC_DPA_W_PH_DSP: +        switch (op2) { +        case OPC_DPAU_H_QBL: +            check_dsp(ctx); +            gen_helper_dpau_h_qbl(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPAU_H_QBR: +            check_dsp(ctx); +            gen_helper_dpau_h_qbr(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPSU_H_QBL: +            check_dsp(ctx); +            gen_helper_dpsu_h_qbl(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPSU_H_QBR: +            check_dsp(ctx); +            gen_helper_dpsu_h_qbr(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPA_W_PH: +            check_dspr2(ctx); +            gen_helper_dpa_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPAX_W_PH: +            check_dspr2(ctx); +            gen_helper_dpax_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPAQ_S_W_PH: +            check_dsp(ctx); +            gen_helper_dpaq_s_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPAQX_S_W_PH: +            check_dspr2(ctx); +            gen_helper_dpaqx_s_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPAQX_SA_W_PH: +            check_dspr2(ctx); +            gen_helper_dpaqx_sa_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPS_W_PH: +            check_dspr2(ctx); +            gen_helper_dps_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPSX_W_PH: +            check_dspr2(ctx); +            gen_helper_dpsx_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPSQ_S_W_PH: +            check_dsp(ctx); +            gen_helper_dpsq_s_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPSQX_S_W_PH: +            check_dspr2(ctx); +            gen_helper_dpsqx_s_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPSQX_SA_W_PH: +            check_dspr2(ctx); +            gen_helper_dpsqx_sa_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_MULSAQ_S_W_PH: +            check_dsp(ctx); +            gen_helper_mulsaq_s_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPAQ_SA_L_W: +            check_dsp(ctx); +            gen_helper_dpaq_sa_l_w(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_DPSQ_SA_L_W: +            check_dsp(ctx); +            gen_helper_dpsq_sa_l_w(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_MAQ_S_W_PHL: +            check_dsp(ctx); +            gen_helper_maq_s_w_phl(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_MAQ_S_W_PHR: +            check_dsp(ctx); +            gen_helper_maq_s_w_phr(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_MAQ_SA_W_PHL: +            check_dsp(ctx); +            gen_helper_maq_sa_w_phl(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_MAQ_SA_W_PHR: +            check_dsp(ctx); +            gen_helper_maq_sa_w_phr(t0, v1_t, v2_t, cpu_env); +            break; +        case OPC_MULSA_W_PH: +            check_dspr2(ctx); +            gen_helper_mulsa_w_ph(t0, v1_t, v2_t, cpu_env); +            break; +        } +        break; +#ifdef TARGET_MIPS64 +    case OPC_DPAQ_W_QH_DSP: +        { +            int ac = ret & 0x03; +            tcg_gen_movi_i32(t0, ac); + +            switch (op2) { +            case OPC_DMADD: +                check_dsp(ctx); +                gen_helper_dmadd(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DMADDU: +                check_dsp(ctx); +                gen_helper_dmaddu(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DMSUB: +                check_dsp(ctx); +                gen_helper_dmsub(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DMSUBU: +                check_dsp(ctx); +                gen_helper_dmsubu(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DPA_W_QH: +                check_dspr2(ctx); +                gen_helper_dpa_w_qh(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DPAQ_S_W_QH: +                check_dsp(ctx); +                gen_helper_dpaq_s_w_qh(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DPAQ_SA_L_PW: +                check_dsp(ctx); +                gen_helper_dpaq_sa_l_pw(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DPAU_H_OBL: +                check_dsp(ctx); +                gen_helper_dpau_h_obl(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DPAU_H_OBR: +                check_dsp(ctx); +                gen_helper_dpau_h_obr(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DPS_W_QH: +                check_dspr2(ctx); +                gen_helper_dps_w_qh(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DPSQ_S_W_QH: +                check_dsp(ctx); +                gen_helper_dpsq_s_w_qh(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DPSQ_SA_L_PW: +                check_dsp(ctx); +                gen_helper_dpsq_sa_l_pw(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DPSU_H_OBL: +                check_dsp(ctx); +                gen_helper_dpsu_h_obl(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_DPSU_H_OBR: +                check_dsp(ctx); +                gen_helper_dpsu_h_obr(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MAQ_S_L_PWL: +                check_dsp(ctx); +                gen_helper_maq_s_l_pwl(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MAQ_S_L_PWR: +                check_dsp(ctx); +                gen_helper_maq_s_l_pwr(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MAQ_S_W_QHLL: +                check_dsp(ctx); +                gen_helper_maq_s_w_qhll(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MAQ_SA_W_QHLL: +                check_dsp(ctx); +                gen_helper_maq_sa_w_qhll(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MAQ_S_W_QHLR: +                check_dsp(ctx); +                gen_helper_maq_s_w_qhlr(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MAQ_SA_W_QHLR: +                check_dsp(ctx); +                gen_helper_maq_sa_w_qhlr(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MAQ_S_W_QHRL: +                check_dsp(ctx); +                gen_helper_maq_s_w_qhrl(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MAQ_SA_W_QHRL: +                check_dsp(ctx); +                gen_helper_maq_sa_w_qhrl(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MAQ_S_W_QHRR: +                check_dsp(ctx); +                gen_helper_maq_s_w_qhrr(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MAQ_SA_W_QHRR: +                check_dsp(ctx); +                gen_helper_maq_sa_w_qhrr(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MULSAQ_S_L_PW: +                check_dsp(ctx); +                gen_helper_mulsaq_s_l_pw(v1_t, v2_t, t0, cpu_env); +                break; +            case OPC_MULSAQ_S_W_QH: +                check_dsp(ctx); +                gen_helper_mulsaq_s_w_qh(v1_t, v2_t, t0, cpu_env); +                break; +            } +        } +        break; +#endif +    case OPC_ADDU_QB_DSP: +        switch (op2) { +        case OPC_MULEU_S_PH_QBL: +            check_dsp(ctx); +            gen_helper_muleu_s_ph_qbl(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULEU_S_PH_QBR: +            check_dsp(ctx); +            gen_helper_muleu_s_ph_qbr(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULQ_RS_PH: +            check_dsp(ctx); +            gen_helper_mulq_rs_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULEQ_S_W_PHL: +            check_dsp(ctx); +            gen_helper_muleq_s_w_phl(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULEQ_S_W_PHR: +            check_dsp(ctx); +            gen_helper_muleq_s_w_phr(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULQ_S_PH: +            check_dspr2(ctx); +            gen_helper_mulq_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        } +        break; +#ifdef TARGET_MIPS64 +    case OPC_ADDU_OB_DSP: +        switch (op2) { +        case OPC_MULEQ_S_PW_QHL: +            check_dsp(ctx); +            gen_helper_muleq_s_pw_qhl(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULEQ_S_PW_QHR: +            check_dsp(ctx); +            gen_helper_muleq_s_pw_qhr(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULEU_S_QH_OBL: +            check_dsp(ctx); +            gen_helper_muleu_s_qh_obl(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULEU_S_QH_OBR: +            check_dsp(ctx); +            gen_helper_muleu_s_qh_obr(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_MULQ_RS_QH: +            check_dsp(ctx); +            gen_helper_mulq_rs_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        } +        break; +#endif +    } + +    tcg_temp_free_i32(t0); +    tcg_temp_free(v1_t); +    tcg_temp_free(v2_t); + +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s", opn); + +} + +static void gen_mipsdsp_bitinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, +                                int ret, int val) +{ +    const char *opn = "mipsdsp Bit/ Manipulation"; +    int16_t imm; +    TCGv t0; +    TCGv val_t; + +    if (ret == 0) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    val_t = tcg_temp_new(); +    gen_load_gpr(val_t, val); + +    switch (op1) { +    case OPC_ABSQ_S_PH_DSP: +        switch (op2) { +        case OPC_BITREV: +            check_dsp(ctx); +            gen_helper_bitrev(cpu_gpr[ret], val_t); +            break; +        case OPC_REPL_QB: +            check_dsp(ctx); +            { +                target_long result; +                imm = (ctx->opcode >> 16) & 0xFF; +                result = (uint32_t)imm << 24 | +                         (uint32_t)imm << 16 | +                         (uint32_t)imm << 8  | +                         (uint32_t)imm; +                result = (int32_t)result; +                tcg_gen_movi_tl(cpu_gpr[ret], result); +            } +            break; +        case OPC_REPLV_QB: +            check_dsp(ctx); +            tcg_gen_ext8u_tl(cpu_gpr[ret], val_t); +            tcg_gen_shli_tl(t0, cpu_gpr[ret], 8); +            tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); +            tcg_gen_shli_tl(t0, cpu_gpr[ret], 16); +            tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); +            tcg_gen_ext32s_tl(cpu_gpr[ret], cpu_gpr[ret]); +            break; +        case OPC_REPL_PH: +            check_dsp(ctx); +            { +                imm = (ctx->opcode >> 16) & 0x03FF; +                imm = (int16_t)(imm << 6) >> 6; +                tcg_gen_movi_tl(cpu_gpr[ret], \ +                                (target_long)((int32_t)imm << 16 | \ +                                (uint16_t)imm)); +            } +            break; +        case OPC_REPLV_PH: +            check_dsp(ctx); +            tcg_gen_ext16u_tl(cpu_gpr[ret], val_t); +            tcg_gen_shli_tl(t0, cpu_gpr[ret], 16); +            tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); +            tcg_gen_ext32s_tl(cpu_gpr[ret], cpu_gpr[ret]); +            break; +        } +        break; +#ifdef TARGET_MIPS64 +    case OPC_ABSQ_S_QH_DSP: +        switch (op2) { +        case OPC_REPL_OB: +            check_dsp(ctx); +            { +                target_long temp; + +                imm = (ctx->opcode >> 16) & 0xFF; +                temp = ((uint64_t)imm << 8) | (uint64_t)imm; +                temp = (temp << 16) | temp; +                temp = (temp << 32) | temp; +                tcg_gen_movi_tl(cpu_gpr[ret], temp); +                break; +            } +        case OPC_REPL_PW: +            check_dsp(ctx); +            { +                target_long temp; + +                imm = (ctx->opcode >> 16) & 0x03FF; +                imm = (int16_t)(imm << 6) >> 6; +                temp = ((target_long)imm << 32) \ +                       | ((target_long)imm & 0xFFFFFFFF); +                tcg_gen_movi_tl(cpu_gpr[ret], temp); +                break; +            } +        case OPC_REPL_QH: +            check_dsp(ctx); +            { +                target_long temp; + +                imm = (ctx->opcode >> 16) & 0x03FF; +                imm = (int16_t)(imm << 6) >> 6; + +                temp = ((uint64_t)(uint16_t)imm << 48) | +                       ((uint64_t)(uint16_t)imm << 32) | +                       ((uint64_t)(uint16_t)imm << 16) | +                       (uint64_t)(uint16_t)imm; +                tcg_gen_movi_tl(cpu_gpr[ret], temp); +                break; +            } +        case OPC_REPLV_OB: +            check_dsp(ctx); +            tcg_gen_ext8u_tl(cpu_gpr[ret], val_t); +            tcg_gen_shli_tl(t0, cpu_gpr[ret], 8); +            tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); +            tcg_gen_shli_tl(t0, cpu_gpr[ret], 16); +            tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); +            tcg_gen_shli_tl(t0, cpu_gpr[ret], 32); +            tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); +            break; +        case OPC_REPLV_PW: +            check_dsp(ctx); +            tcg_gen_ext32u_i64(cpu_gpr[ret], val_t); +            tcg_gen_shli_tl(t0, cpu_gpr[ret], 32); +            tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); +            break; +        case OPC_REPLV_QH: +            check_dsp(ctx); +            tcg_gen_ext16u_tl(cpu_gpr[ret], val_t); +            tcg_gen_shli_tl(t0, cpu_gpr[ret], 16); +            tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); +            tcg_gen_shli_tl(t0, cpu_gpr[ret], 32); +            tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); +            break; +        } +        break; +#endif +    } +    tcg_temp_free(t0); +    tcg_temp_free(val_t); + +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s", opn); +} + +static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, +                                     uint32_t op1, uint32_t op2, +                                     int ret, int v1, int v2, int check_ret) +{ +    const char *opn = "mipsdsp add compare pick"; +    TCGv t1; +    TCGv v1_t; +    TCGv v2_t; + +    if ((ret == 0) && (check_ret == 1)) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t1 = tcg_temp_new(); +    v1_t = tcg_temp_new(); +    v2_t = tcg_temp_new(); + +    gen_load_gpr(v1_t, v1); +    gen_load_gpr(v2_t, v2); + +    switch (op1) { +    case OPC_CMPU_EQ_QB_DSP: +        switch (op2) { +        case OPC_CMPU_EQ_QB: +            check_dsp(ctx); +            gen_helper_cmpu_eq_qb(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMPU_LT_QB: +            check_dsp(ctx); +            gen_helper_cmpu_lt_qb(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMPU_LE_QB: +            check_dsp(ctx); +            gen_helper_cmpu_le_qb(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMPGU_EQ_QB: +            check_dsp(ctx); +            gen_helper_cmpgu_eq_qb(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_CMPGU_LT_QB: +            check_dsp(ctx); +            gen_helper_cmpgu_lt_qb(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_CMPGU_LE_QB: +            check_dsp(ctx); +            gen_helper_cmpgu_le_qb(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_CMPGDU_EQ_QB: +            check_dspr2(ctx); +            gen_helper_cmpgu_eq_qb(t1, v1_t, v2_t); +            tcg_gen_mov_tl(cpu_gpr[ret], t1); +            tcg_gen_andi_tl(cpu_dspctrl, cpu_dspctrl, 0xF0FFFFFF); +            tcg_gen_shli_tl(t1, t1, 24); +            tcg_gen_or_tl(cpu_dspctrl, cpu_dspctrl, t1); +            break; +        case OPC_CMPGDU_LT_QB: +            check_dspr2(ctx); +            gen_helper_cmpgu_lt_qb(t1, v1_t, v2_t); +            tcg_gen_mov_tl(cpu_gpr[ret], t1); +            tcg_gen_andi_tl(cpu_dspctrl, cpu_dspctrl, 0xF0FFFFFF); +            tcg_gen_shli_tl(t1, t1, 24); +            tcg_gen_or_tl(cpu_dspctrl, cpu_dspctrl, t1); +            break; +        case OPC_CMPGDU_LE_QB: +            check_dspr2(ctx); +            gen_helper_cmpgu_le_qb(t1, v1_t, v2_t); +            tcg_gen_mov_tl(cpu_gpr[ret], t1); +            tcg_gen_andi_tl(cpu_dspctrl, cpu_dspctrl, 0xF0FFFFFF); +            tcg_gen_shli_tl(t1, t1, 24); +            tcg_gen_or_tl(cpu_dspctrl, cpu_dspctrl, t1); +            break; +        case OPC_CMP_EQ_PH: +            check_dsp(ctx); +            gen_helper_cmp_eq_ph(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMP_LT_PH: +            check_dsp(ctx); +            gen_helper_cmp_lt_ph(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMP_LE_PH: +            check_dsp(ctx); +            gen_helper_cmp_le_ph(v1_t, v2_t, cpu_env); +            break; +        case OPC_PICK_QB: +            check_dsp(ctx); +            gen_helper_pick_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_PICK_PH: +            check_dsp(ctx); +            gen_helper_pick_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_PACKRL_PH: +            check_dsp(ctx); +            gen_helper_packrl_ph(cpu_gpr[ret], v1_t, v2_t); +            break; +        } +        break; +#ifdef TARGET_MIPS64 +    case OPC_CMPU_EQ_OB_DSP: +        switch (op2) { +        case OPC_CMP_EQ_PW: +            check_dsp(ctx); +            gen_helper_cmp_eq_pw(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMP_LT_PW: +            check_dsp(ctx); +            gen_helper_cmp_lt_pw(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMP_LE_PW: +            check_dsp(ctx); +            gen_helper_cmp_le_pw(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMP_EQ_QH: +            check_dsp(ctx); +            gen_helper_cmp_eq_qh(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMP_LT_QH: +            check_dsp(ctx); +            gen_helper_cmp_lt_qh(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMP_LE_QH: +            check_dsp(ctx); +            gen_helper_cmp_le_qh(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMPGDU_EQ_OB: +            check_dspr2(ctx); +            gen_helper_cmpgdu_eq_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_CMPGDU_LT_OB: +            check_dspr2(ctx); +            gen_helper_cmpgdu_lt_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_CMPGDU_LE_OB: +            check_dspr2(ctx); +            gen_helper_cmpgdu_le_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_CMPGU_EQ_OB: +            check_dsp(ctx); +            gen_helper_cmpgu_eq_ob(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_CMPGU_LT_OB: +            check_dsp(ctx); +            gen_helper_cmpgu_lt_ob(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_CMPGU_LE_OB: +            check_dsp(ctx); +            gen_helper_cmpgu_le_ob(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_CMPU_EQ_OB: +            check_dsp(ctx); +            gen_helper_cmpu_eq_ob(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMPU_LT_OB: +            check_dsp(ctx); +            gen_helper_cmpu_lt_ob(v1_t, v2_t, cpu_env); +            break; +        case OPC_CMPU_LE_OB: +            check_dsp(ctx); +            gen_helper_cmpu_le_ob(v1_t, v2_t, cpu_env); +            break; +        case OPC_PACKRL_PW: +            check_dsp(ctx); +            gen_helper_packrl_pw(cpu_gpr[ret], v1_t, v2_t); +            break; +        case OPC_PICK_OB: +            check_dsp(ctx); +            gen_helper_pick_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_PICK_PW: +            check_dsp(ctx); +            gen_helper_pick_pw(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        case OPC_PICK_QH: +            check_dsp(ctx); +            gen_helper_pick_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); +            break; +        } +        break; +#endif +    } + +    tcg_temp_free(t1); +    tcg_temp_free(v1_t); +    tcg_temp_free(v2_t); + +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s", opn); +} + +static void gen_mipsdsp_append(CPUMIPSState *env, DisasContext *ctx, +                               uint32_t op1, int rt, int rs, int sa) +{ +    const char *opn = "mipsdsp append/dappend"; +    TCGv t0; + +    check_dspr2(ctx); + +    if (rt == 0) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    gen_load_gpr(t0, rs); + +    switch (op1) { +    case OPC_APPEND_DSP: +        switch (MASK_APPEND(ctx->opcode)) { +        case OPC_APPEND: +            if (sa != 0) { +                tcg_gen_deposit_tl(cpu_gpr[rt], t0, cpu_gpr[rt], sa, 32 - sa); +            } +            tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); +            break; +        case OPC_PREPEND: +            if (sa != 0) { +                tcg_gen_ext32u_tl(cpu_gpr[rt], cpu_gpr[rt]); +                tcg_gen_shri_tl(cpu_gpr[rt], cpu_gpr[rt], sa); +                tcg_gen_shli_tl(t0, t0, 32 - sa); +                tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); +            } +            tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); +            break; +        case OPC_BALIGN: +            sa &= 3; +            if (sa != 0 && sa != 2) { +                tcg_gen_shli_tl(cpu_gpr[rt], cpu_gpr[rt], 8 * sa); +                tcg_gen_ext32u_tl(t0, t0); +                tcg_gen_shri_tl(t0, t0, 8 * (4 - sa)); +                tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); +            } +            tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK APPEND"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +#ifdef TARGET_MIPS64 +    case OPC_DAPPEND_DSP: +        switch (MASK_DAPPEND(ctx->opcode)) { +        case OPC_DAPPEND: +            if (sa != 0) { +                tcg_gen_deposit_tl(cpu_gpr[rt], t0, cpu_gpr[rt], sa, 64 - sa); +            } +            break; +        case OPC_PREPENDD: +            tcg_gen_shri_tl(cpu_gpr[rt], cpu_gpr[rt], 0x20 | sa); +            tcg_gen_shli_tl(t0, t0, 64 - (0x20 | sa)); +            tcg_gen_or_tl(cpu_gpr[rt], t0, t0); +            break; +        case OPC_PREPENDW: +            if (sa != 0) { +                tcg_gen_shri_tl(cpu_gpr[rt], cpu_gpr[rt], sa); +                tcg_gen_shli_tl(t0, t0, 64 - sa); +                tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); +            } +            break; +        case OPC_DBALIGN: +            sa &= 7; +            if (sa != 0 && sa != 2 && sa != 4) { +                tcg_gen_shli_tl(cpu_gpr[rt], cpu_gpr[rt], 8 * sa); +                tcg_gen_shri_tl(t0, t0, 8 * (8 - sa)); +                tcg_gen_or_tl(cpu_gpr[rt], cpu_gpr[rt], t0); +            } +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK DAPPEND"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +#endif +    } +    tcg_temp_free(t0); +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s", opn); +} + +static void gen_mipsdsp_accinsn(DisasContext *ctx, uint32_t op1, uint32_t op2, +                                int ret, int v1, int v2, int check_ret) + +{ +    const char *opn = "mipsdsp accumulator"; +    TCGv t0; +    TCGv t1; +    TCGv v1_t; +    TCGv v2_t; +    int16_t imm; + +    if ((ret == 0) && (check_ret == 1)) { +        /* Treat as NOP. */ +        MIPS_DEBUG("NOP"); +        return; +    } + +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); +    v1_t = tcg_temp_new(); +    v2_t = tcg_temp_new(); + +    gen_load_gpr(v1_t, v1); +    gen_load_gpr(v2_t, v2); + +    switch (op1) { +    case OPC_EXTR_W_DSP: +        check_dsp(ctx); +        switch (op2) { +        case OPC_EXTR_W: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_extr_w(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_EXTR_R_W: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_extr_r_w(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_EXTR_RS_W: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_extr_rs_w(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_EXTR_S_H: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_extr_s_h(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_EXTRV_S_H: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_extr_s_h(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_EXTRV_W: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_extr_w(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_EXTRV_R_W: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_extr_r_w(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_EXTRV_RS_W: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_extr_rs_w(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_EXTP: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_extp(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_EXTPV: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_extp(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_EXTPDP: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_extpdp(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_EXTPDPV: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_extpdp(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_SHILO: +            imm = (ctx->opcode >> 20) & 0x3F; +            tcg_gen_movi_tl(t0, ret); +            tcg_gen_movi_tl(t1, imm); +            gen_helper_shilo(t0, t1, cpu_env); +            break; +        case OPC_SHILOV: +            tcg_gen_movi_tl(t0, ret); +            gen_helper_shilo(t0, v1_t, cpu_env); +            break; +        case OPC_MTHLIP: +            tcg_gen_movi_tl(t0, ret); +            gen_helper_mthlip(t0, v1_t, cpu_env); +            break; +        case OPC_WRDSP: +            imm = (ctx->opcode >> 11) & 0x3FF; +            tcg_gen_movi_tl(t0, imm); +            gen_helper_wrdsp(v1_t, t0, cpu_env); +            break; +        case OPC_RDDSP: +            imm = (ctx->opcode >> 16) & 0x03FF; +            tcg_gen_movi_tl(t0, imm); +            gen_helper_rddsp(cpu_gpr[ret], t0, cpu_env); +            break; +        } +        break; +#ifdef TARGET_MIPS64 +    case OPC_DEXTR_W_DSP: +        check_dsp(ctx); +        switch (op2) { +        case OPC_DMTHLIP: +            tcg_gen_movi_tl(t0, ret); +            gen_helper_dmthlip(v1_t, t0, cpu_env); +            break; +        case OPC_DSHILO: +            { +                int shift = (ctx->opcode >> 19) & 0x7F; +                int ac = (ctx->opcode >> 11) & 0x03; +                tcg_gen_movi_tl(t0, shift); +                tcg_gen_movi_tl(t1, ac); +                gen_helper_dshilo(t0, t1, cpu_env); +                break; +            } +        case OPC_DSHILOV: +            { +                int ac = (ctx->opcode >> 11) & 0x03; +                tcg_gen_movi_tl(t0, ac); +                gen_helper_dshilo(v1_t, t0, cpu_env); +                break; +            } +        case OPC_DEXTP: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); + +            gen_helper_dextp(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_DEXTPV: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_dextp(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_DEXTPDP: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_dextpdp(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_DEXTPDPV: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_dextpdp(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_DEXTR_L: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_dextr_l(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_DEXTR_R_L: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_dextr_r_l(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_DEXTR_RS_L: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_dextr_rs_l(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_DEXTR_W: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_dextr_w(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_DEXTR_R_W: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_dextr_r_w(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_DEXTR_RS_W: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_dextr_rs_w(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_DEXTR_S_H: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_dextr_s_h(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_DEXTRV_S_H: +            tcg_gen_movi_tl(t0, v2); +            tcg_gen_movi_tl(t1, v1); +            gen_helper_dextr_s_h(cpu_gpr[ret], t0, t1, cpu_env); +            break; +        case OPC_DEXTRV_L: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_dextr_l(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_DEXTRV_R_L: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_dextr_r_l(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_DEXTRV_RS_L: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_dextr_rs_l(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_DEXTRV_W: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_dextr_w(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_DEXTRV_R_W: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_dextr_r_w(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        case OPC_DEXTRV_RS_W: +            tcg_gen_movi_tl(t0, v2); +            gen_helper_dextr_rs_w(cpu_gpr[ret], t0, v1_t, cpu_env); +            break; +        } +        break; +#endif +    } + +    tcg_temp_free(t0); +    tcg_temp_free(t1); +    tcg_temp_free(v1_t); +    tcg_temp_free(v2_t); + +    (void)opn; /* avoid a compiler warning */ +    MIPS_DEBUG("%s", opn); +} + +/* End MIPSDSP functions. */ + +static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) +{ +    int rs, rt, rd, sa; +    uint32_t op1, op2; + +    rs = (ctx->opcode >> 21) & 0x1f; +    rt = (ctx->opcode >> 16) & 0x1f; +    rd = (ctx->opcode >> 11) & 0x1f; +    sa = (ctx->opcode >> 6) & 0x1f; + +    op1 = MASK_SPECIAL(ctx->opcode); +    switch (op1) { +    case OPC_LSA: +        gen_lsa(ctx, op1, rd, rs, rt, extract32(ctx->opcode, 6, 2)); +        break; +    case OPC_MULT ... OPC_DIVU: +        op2 = MASK_R6_MULDIV(ctx->opcode); +        switch (op2) { +        case R6_OPC_MUL: +        case R6_OPC_MUH: +        case R6_OPC_MULU: +        case R6_OPC_MUHU: +        case R6_OPC_DIV: +        case R6_OPC_MOD: +        case R6_OPC_DIVU: +        case R6_OPC_MODU: +            gen_r6_muldiv(ctx, op2, rd, rs, rt); +            break; +        default: +            MIPS_INVAL("special_r6 muldiv"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_SELEQZ: +    case OPC_SELNEZ: +        gen_cond_move(ctx, op1, rd, rs, rt); +        break; +    case R6_OPC_CLO: +    case R6_OPC_CLZ: +        if (rt == 0 && sa == 1) { +            /* Major opcode and function field is shared with preR6 MFHI/MTHI. +               We need additionally to check other fields */ +            gen_cl(ctx, op1, rd, rs); +        } else { +            generate_exception(ctx, EXCP_RI); +        } +        break; +    case R6_OPC_SDBBP: +        if (is_uhi(extract32(ctx->opcode, 6, 20))) { +            gen_helper_do_semihosting(cpu_env); +        } else { +            if (ctx->hflags & MIPS_HFLAG_SBRI) { +                generate_exception(ctx, EXCP_RI); +            } else { +                generate_exception(ctx, EXCP_DBp); +            } +        } +        break; +#if defined(TARGET_MIPS64) +    case OPC_DLSA: +        check_mips_64(ctx); +        gen_lsa(ctx, op1, rd, rs, rt, extract32(ctx->opcode, 6, 2)); +        break; +    case R6_OPC_DCLO: +    case R6_OPC_DCLZ: +        if (rt == 0 && sa == 1) { +            /* Major opcode and function field is shared with preR6 MFHI/MTHI. +               We need additionally to check other fields */ +            check_mips_64(ctx); +            gen_cl(ctx, op1, rd, rs); +        } else { +            generate_exception(ctx, EXCP_RI); +        } +        break; +    case OPC_DMULT ... OPC_DDIVU: +        op2 = MASK_R6_MULDIV(ctx->opcode); +        switch (op2) { +        case R6_OPC_DMUL: +        case R6_OPC_DMUH: +        case R6_OPC_DMULU: +        case R6_OPC_DMUHU: +        case R6_OPC_DDIV: +        case R6_OPC_DMOD: +        case R6_OPC_DDIVU: +        case R6_OPC_DMODU: +            check_mips_64(ctx); +            gen_r6_muldiv(ctx, op2, rd, rs, rt); +            break; +        default: +            MIPS_INVAL("special_r6 muldiv"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +#endif +    default:            /* Invalid */ +        MIPS_INVAL("special_r6"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx) +{ +    int rs, rt, rd, sa; +    uint32_t op1; + +    rs = (ctx->opcode >> 21) & 0x1f; +    rt = (ctx->opcode >> 16) & 0x1f; +    rd = (ctx->opcode >> 11) & 0x1f; +    sa = (ctx->opcode >> 6) & 0x1f; + +    op1 = MASK_SPECIAL(ctx->opcode); +    switch (op1) { +    case OPC_MOVN:         /* Conditional move */ +    case OPC_MOVZ: +        check_insn(ctx, ISA_MIPS4 | ISA_MIPS32 | +                   INSN_LOONGSON2E | INSN_LOONGSON2F); +        gen_cond_move(ctx, op1, rd, rs, rt); +        break; +    case OPC_MFHI:          /* Move from HI/LO */ +    case OPC_MFLO: +        gen_HILO(ctx, op1, rs & 3, rd); +        break; +    case OPC_MTHI: +    case OPC_MTLO:          /* Move to HI/LO */ +        gen_HILO(ctx, op1, rd & 3, rs); +        break; +    case OPC_MOVCI: +        check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); +        if (env->CP0_Config1 & (1 << CP0C1_FP)) { +            check_cp1_enabled(ctx); +            gen_movci(ctx, rd, rs, (ctx->opcode >> 18) & 0x7, +                      (ctx->opcode >> 16) & 1); +        } else { +            generate_exception_err(ctx, EXCP_CpU, 1); +        } +        break; +    case OPC_MULT: +    case OPC_MULTU: +        if (sa) { +            check_insn(ctx, INSN_VR54XX); +            op1 = MASK_MUL_VR54XX(ctx->opcode); +            gen_mul_vr54xx(ctx, op1, rd, rs, rt); +        } else { +            gen_muldiv(ctx, op1, rd & 3, rs, rt); +        } +        break; +    case OPC_DIV: +    case OPC_DIVU: +        gen_muldiv(ctx, op1, 0, rs, rt); +        break; +#if defined(TARGET_MIPS64) +    case OPC_DMULT ... OPC_DDIVU: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_muldiv(ctx, op1, 0, rs, rt); +        break; +#endif +    case OPC_JR: +        gen_compute_branch(ctx, op1, 4, rs, rd, sa, 4); +        break; +    case OPC_SPIM: +#ifdef MIPS_STRICT_STANDARD +        MIPS_INVAL("SPIM"); +        generate_exception(ctx, EXCP_RI); +#else +        /* Implemented as RI exception for now. */ +        MIPS_INVAL("spim (unofficial)"); +        generate_exception(ctx, EXCP_RI); +#endif +        break; +    default:            /* Invalid */ +        MIPS_INVAL("special_legacy"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) +{ +    int rs, rt, rd, sa; +    uint32_t op1; + +    rs = (ctx->opcode >> 21) & 0x1f; +    rt = (ctx->opcode >> 16) & 0x1f; +    rd = (ctx->opcode >> 11) & 0x1f; +    sa = (ctx->opcode >> 6) & 0x1f; + +    op1 = MASK_SPECIAL(ctx->opcode); +    switch (op1) { +    case OPC_SLL:          /* Shift with immediate */ +        if (sa == 5 && rd == 0 && +            rs == 0 && rt == 0) { /* PAUSE */ +            if ((ctx->insn_flags & ISA_MIPS32R6) && +                (ctx->hflags & MIPS_HFLAG_BMASK)) { +                MIPS_DEBUG("CTI in delay / forbidden slot"); +                generate_exception(ctx, EXCP_RI); +                break; +            } +        } +        /* Fallthrough */ +    case OPC_SRA: +        gen_shift_imm(ctx, op1, rd, rt, sa); +        break; +    case OPC_SRL: +        switch ((ctx->opcode >> 21) & 0x1f) { +        case 1: +            /* rotr is decoded as srl on non-R2 CPUs */ +            if (ctx->insn_flags & ISA_MIPS32R2) { +                op1 = OPC_ROTR; +            } +            /* Fallthrough */ +        case 0: +            gen_shift_imm(ctx, op1, rd, rt, sa); +            break; +        default: +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_ADD ... OPC_SUBU: +        gen_arith(ctx, op1, rd, rs, rt); +        break; +    case OPC_SLLV:         /* Shifts */ +    case OPC_SRAV: +        gen_shift(ctx, op1, rd, rs, rt); +        break; +    case OPC_SRLV: +        switch ((ctx->opcode >> 6) & 0x1f) { +        case 1: +            /* rotrv is decoded as srlv on non-R2 CPUs */ +            if (ctx->insn_flags & ISA_MIPS32R2) { +                op1 = OPC_ROTRV; +            } +            /* Fallthrough */ +        case 0: +            gen_shift(ctx, op1, rd, rs, rt); +            break; +        default: +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_SLT:          /* Set on less than */ +    case OPC_SLTU: +        gen_slt(ctx, op1, rd, rs, rt); +        break; +    case OPC_AND:          /* Logic*/ +    case OPC_OR: +    case OPC_NOR: +    case OPC_XOR: +        gen_logic(ctx, op1, rd, rs, rt); +        break; +    case OPC_JALR: +        gen_compute_branch(ctx, op1, 4, rs, rd, sa, 4); +        break; +    case OPC_TGE ... OPC_TEQ: /* Traps */ +    case OPC_TNE: +        check_insn(ctx, ISA_MIPS2); +        gen_trap(ctx, op1, rs, rt, -1); +        break; +    case OPC_LSA: /* OPC_PMON */ +        if ((ctx->insn_flags & ISA_MIPS32R6) || +            (env->CP0_Config3 & (1 << CP0C3_MSAP))) { +            decode_opc_special_r6(env, ctx); +        } else { +            /* Pmon entry point, also R4010 selsl */ +#ifdef MIPS_STRICT_STANDARD +            MIPS_INVAL("PMON / selsl"); +            generate_exception(ctx, EXCP_RI); +#else +            gen_helper_0e0i(pmon, sa); +#endif +        } +        break; +    case OPC_SYSCALL: +        generate_exception(ctx, EXCP_SYSCALL); +        ctx->bstate = BS_STOP; +        break; +    case OPC_BREAK: +        generate_exception(ctx, EXCP_BREAK); +        break; +    case OPC_SYNC: +        check_insn(ctx, ISA_MIPS2); +        /* Treat as NOP. */ +        break; + +#if defined(TARGET_MIPS64) +        /* MIPS64 specific opcodes */ +    case OPC_DSLL: +    case OPC_DSRA: +    case OPC_DSLL32: +    case OPC_DSRA32: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_shift_imm(ctx, op1, rd, rt, sa); +        break; +    case OPC_DSRL: +        switch ((ctx->opcode >> 21) & 0x1f) { +        case 1: +            /* drotr is decoded as dsrl on non-R2 CPUs */ +            if (ctx->insn_flags & ISA_MIPS32R2) { +                op1 = OPC_DROTR; +            } +            /* Fallthrough */ +        case 0: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_shift_imm(ctx, op1, rd, rt, sa); +            break; +        default: +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_DSRL32: +        switch ((ctx->opcode >> 21) & 0x1f) { +        case 1: +            /* drotr32 is decoded as dsrl32 on non-R2 CPUs */ +            if (ctx->insn_flags & ISA_MIPS32R2) { +                op1 = OPC_DROTR32; +            } +            /* Fallthrough */ +        case 0: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_shift_imm(ctx, op1, rd, rt, sa); +            break; +        default: +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_DADD ... OPC_DSUBU: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_arith(ctx, op1, rd, rs, rt); +        break; +    case OPC_DSLLV: +    case OPC_DSRAV: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_shift(ctx, op1, rd, rs, rt); +        break; +    case OPC_DSRLV: +        switch ((ctx->opcode >> 6) & 0x1f) { +        case 1: +            /* drotrv is decoded as dsrlv on non-R2 CPUs */ +            if (ctx->insn_flags & ISA_MIPS32R2) { +                op1 = OPC_DROTRV; +            } +            /* Fallthrough */ +        case 0: +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_shift(ctx, op1, rd, rs, rt); +            break; +        default: +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_DLSA: +        if ((ctx->insn_flags & ISA_MIPS32R6) || +            (env->CP0_Config3 & (1 << CP0C3_MSAP))) { +            decode_opc_special_r6(env, ctx); +        } +        break; +#endif +    default: +        if (ctx->insn_flags & ISA_MIPS32R6) { +            decode_opc_special_r6(env, ctx); +        } else { +            decode_opc_special_legacy(env, ctx); +        } +    } +} + +static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) +{ +    int rs, rt, rd; +    uint32_t op1; + +    check_insn_opc_removed(ctx, ISA_MIPS32R6); + +    rs = (ctx->opcode >> 21) & 0x1f; +    rt = (ctx->opcode >> 16) & 0x1f; +    rd = (ctx->opcode >> 11) & 0x1f; + +    op1 = MASK_SPECIAL2(ctx->opcode); +    switch (op1) { +    case OPC_MADD ... OPC_MADDU: /* Multiply and add/sub */ +    case OPC_MSUB ... OPC_MSUBU: +        check_insn(ctx, ISA_MIPS32); +        gen_muldiv(ctx, op1, rd & 3, rs, rt); +        break; +    case OPC_MUL: +        gen_arith(ctx, op1, rd, rs, rt); +        break; +    case OPC_DIV_G_2F: +    case OPC_DIVU_G_2F: +    case OPC_MULT_G_2F: +    case OPC_MULTU_G_2F: +    case OPC_MOD_G_2F: +    case OPC_MODU_G_2F: +        check_insn(ctx, INSN_LOONGSON2F); +        gen_loongson_integer(ctx, op1, rd, rs, rt); +        break; +    case OPC_CLO: +    case OPC_CLZ: +        check_insn(ctx, ISA_MIPS32); +        gen_cl(ctx, op1, rd, rs); +        break; +    case OPC_SDBBP: +        if (is_uhi(extract32(ctx->opcode, 6, 20))) { +            gen_helper_do_semihosting(cpu_env); +        } else { +            /* XXX: not clear which exception should be raised +             *      when in debug mode... +             */ +            check_insn(ctx, ISA_MIPS32); +            generate_exception(ctx, EXCP_DBp); +        } +        break; +#if defined(TARGET_MIPS64) +    case OPC_DCLO: +    case OPC_DCLZ: +        check_insn(ctx, ISA_MIPS64); +        check_mips_64(ctx); +        gen_cl(ctx, op1, rd, rs); +        break; +    case OPC_DMULT_G_2F: +    case OPC_DMULTU_G_2F: +    case OPC_DDIV_G_2F: +    case OPC_DDIVU_G_2F: +    case OPC_DMOD_G_2F: +    case OPC_DMODU_G_2F: +        check_insn(ctx, INSN_LOONGSON2F); +        gen_loongson_integer(ctx, op1, rd, rs, rt); +        break; +#endif +    default:            /* Invalid */ +        MIPS_INVAL("special2_legacy"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) +{ +    int rs, rt, rd, sa; +    uint32_t op1, op2; +    int16_t imm; + +    rs = (ctx->opcode >> 21) & 0x1f; +    rt = (ctx->opcode >> 16) & 0x1f; +    rd = (ctx->opcode >> 11) & 0x1f; +    sa = (ctx->opcode >> 6) & 0x1f; +    imm = (int16_t)ctx->opcode >> 7; + +    op1 = MASK_SPECIAL3(ctx->opcode); +    switch (op1) { +    case R6_OPC_PREF: +        if (rt >= 24) { +            /* hint codes 24-31 are reserved and signal RI */ +            generate_exception(ctx, EXCP_RI); +        } +        /* Treat as NOP. */ +        break; +    case R6_OPC_CACHE: +        /* Treat as NOP. */ +        break; +    case R6_OPC_SC: +        gen_st_cond(ctx, op1, rt, rs, imm); +        break; +    case R6_OPC_LL: +        gen_ld(ctx, op1, rt, rs, imm); +        break; +    case OPC_BSHFL: +        { +            if (rd == 0) { +                /* Treat as NOP. */ +                break; +            } +            op2 = MASK_BSHFL(ctx->opcode); +            switch (op2) { +            case OPC_ALIGN ... OPC_ALIGN_END: +                gen_align(ctx, OPC_ALIGN, rd, rs, rt, sa & 3); +                break; +            case OPC_BITSWAP: +                gen_bitswap(ctx, op2, rd, rt); +                break; +            } +        } +        break; +#if defined(TARGET_MIPS64) +    case R6_OPC_SCD: +        gen_st_cond(ctx, op1, rt, rs, imm); +        break; +    case R6_OPC_LLD: +        gen_ld(ctx, op1, rt, rs, imm); +        break; +    case OPC_DBSHFL: +        check_mips_64(ctx); +        { +            if (rd == 0) { +                /* Treat as NOP. */ +                break; +            } +            op2 = MASK_DBSHFL(ctx->opcode); +            switch (op2) { +            case OPC_DALIGN ... OPC_DALIGN_END: +                gen_align(ctx, OPC_DALIGN, rd, rs, rt, sa & 7); +                break; +            case OPC_DBITSWAP: +                gen_bitswap(ctx, op2, rd, rt); +                break; +            } + +        } +        break; +#endif +    default:            /* Invalid */ +        MIPS_INVAL("special3_r6"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) +{ +    int rs, rt, rd; +    uint32_t op1, op2; + +    rs = (ctx->opcode >> 21) & 0x1f; +    rt = (ctx->opcode >> 16) & 0x1f; +    rd = (ctx->opcode >> 11) & 0x1f; + +    op1 = MASK_SPECIAL3(ctx->opcode); +    switch (op1) { +    case OPC_DIV_G_2E ... OPC_DIVU_G_2E: +    case OPC_MOD_G_2E ... OPC_MODU_G_2E: +    case OPC_MULT_G_2E ... OPC_MULTU_G_2E: +        /* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have +         * the same mask and op1. */ +        if ((ctx->insn_flags & ASE_DSPR2) && (op1 == OPC_MULT_G_2E)) { +            op2 = MASK_ADDUH_QB(ctx->opcode); +            switch (op2) { +            case OPC_ADDUH_QB: +            case OPC_ADDUH_R_QB: +            case OPC_ADDQH_PH: +            case OPC_ADDQH_R_PH: +            case OPC_ADDQH_W: +            case OPC_ADDQH_R_W: +            case OPC_SUBUH_QB: +            case OPC_SUBUH_R_QB: +            case OPC_SUBQH_PH: +            case OPC_SUBQH_R_PH: +            case OPC_SUBQH_W: +            case OPC_SUBQH_R_W: +                gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); +                break; +            case OPC_MUL_PH: +            case OPC_MUL_S_PH: +            case OPC_MULQ_S_W: +            case OPC_MULQ_RS_W: +                gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1); +                break; +            default: +                MIPS_INVAL("MASK ADDUH.QB"); +                generate_exception(ctx, EXCP_RI); +                break; +            } +        } else if (ctx->insn_flags & INSN_LOONGSON2E) { +            gen_loongson_integer(ctx, op1, rd, rs, rt); +        } else { +            generate_exception(ctx, EXCP_RI); +        } +        break; +    case OPC_LX_DSP: +        op2 = MASK_LX(ctx->opcode); +        switch (op2) { +#if defined(TARGET_MIPS64) +        case OPC_LDX: +#endif +        case OPC_LBUX: +        case OPC_LHX: +        case OPC_LWX: +            gen_mipsdsp_ld(ctx, op2, rd, rs, rt); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK LX"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_ABSQ_S_PH_DSP: +        op2 = MASK_ABSQ_S_PH(ctx->opcode); +        switch (op2) { +        case OPC_ABSQ_S_QB: +        case OPC_ABSQ_S_PH: +        case OPC_ABSQ_S_W: +        case OPC_PRECEQ_W_PHL: +        case OPC_PRECEQ_W_PHR: +        case OPC_PRECEQU_PH_QBL: +        case OPC_PRECEQU_PH_QBR: +        case OPC_PRECEQU_PH_QBLA: +        case OPC_PRECEQU_PH_QBRA: +        case OPC_PRECEU_PH_QBL: +        case OPC_PRECEU_PH_QBR: +        case OPC_PRECEU_PH_QBLA: +        case OPC_PRECEU_PH_QBRA: +            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); +            break; +        case OPC_BITREV: +        case OPC_REPL_QB: +        case OPC_REPLV_QB: +        case OPC_REPL_PH: +        case OPC_REPLV_PH: +            gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt); +            break; +        default: +            MIPS_INVAL("MASK ABSQ_S.PH"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_ADDU_QB_DSP: +        op2 = MASK_ADDU_QB(ctx->opcode); +        switch (op2) { +        case OPC_ADDQ_PH: +        case OPC_ADDQ_S_PH: +        case OPC_ADDQ_S_W: +        case OPC_ADDU_QB: +        case OPC_ADDU_S_QB: +        case OPC_ADDU_PH: +        case OPC_ADDU_S_PH: +        case OPC_SUBQ_PH: +        case OPC_SUBQ_S_PH: +        case OPC_SUBQ_S_W: +        case OPC_SUBU_QB: +        case OPC_SUBU_S_QB: +        case OPC_SUBU_PH: +        case OPC_SUBU_S_PH: +        case OPC_ADDSC: +        case OPC_ADDWC: +        case OPC_MODSUB: +        case OPC_RADDU_W_QB: +            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); +            break; +        case OPC_MULEU_S_PH_QBL: +        case OPC_MULEU_S_PH_QBR: +        case OPC_MULQ_RS_PH: +        case OPC_MULEQ_S_W_PHL: +        case OPC_MULEQ_S_W_PHR: +        case OPC_MULQ_S_PH: +            gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK ADDU.QB"); +            generate_exception(ctx, EXCP_RI); +            break; + +        } +        break; +    case OPC_CMPU_EQ_QB_DSP: +        op2 = MASK_CMPU_EQ_QB(ctx->opcode); +        switch (op2) { +        case OPC_PRECR_SRA_PH_W: +        case OPC_PRECR_SRA_R_PH_W: +            gen_mipsdsp_arith(ctx, op1, op2, rt, rs, rd); +            break; +        case OPC_PRECR_QB_PH: +        case OPC_PRECRQ_QB_PH: +        case OPC_PRECRQ_PH_W: +        case OPC_PRECRQ_RS_PH_W: +        case OPC_PRECRQU_S_QB_PH: +            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); +            break; +        case OPC_CMPU_EQ_QB: +        case OPC_CMPU_LT_QB: +        case OPC_CMPU_LE_QB: +        case OPC_CMP_EQ_PH: +        case OPC_CMP_LT_PH: +        case OPC_CMP_LE_PH: +            gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 0); +            break; +        case OPC_CMPGU_EQ_QB: +        case OPC_CMPGU_LT_QB: +        case OPC_CMPGU_LE_QB: +        case OPC_CMPGDU_EQ_QB: +        case OPC_CMPGDU_LT_QB: +        case OPC_CMPGDU_LE_QB: +        case OPC_PICK_QB: +        case OPC_PICK_PH: +        case OPC_PACKRL_PH: +            gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 1); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK CMPU.EQ.QB"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_SHLL_QB_DSP: +        gen_mipsdsp_shift(ctx, op1, rd, rs, rt); +        break; +    case OPC_DPA_W_PH_DSP: +        op2 = MASK_DPA_W_PH(ctx->opcode); +        switch (op2) { +        case OPC_DPAU_H_QBL: +        case OPC_DPAU_H_QBR: +        case OPC_DPSU_H_QBL: +        case OPC_DPSU_H_QBR: +        case OPC_DPA_W_PH: +        case OPC_DPAX_W_PH: +        case OPC_DPAQ_S_W_PH: +        case OPC_DPAQX_S_W_PH: +        case OPC_DPAQX_SA_W_PH: +        case OPC_DPS_W_PH: +        case OPC_DPSX_W_PH: +        case OPC_DPSQ_S_W_PH: +        case OPC_DPSQX_S_W_PH: +        case OPC_DPSQX_SA_W_PH: +        case OPC_MULSAQ_S_W_PH: +        case OPC_DPAQ_SA_L_W: +        case OPC_DPSQ_SA_L_W: +        case OPC_MAQ_S_W_PHL: +        case OPC_MAQ_S_W_PHR: +        case OPC_MAQ_SA_W_PHL: +        case OPC_MAQ_SA_W_PHR: +        case OPC_MULSA_W_PH: +            gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK DPAW.PH"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_INSV_DSP: +        op2 = MASK_INSV(ctx->opcode); +        switch (op2) { +        case OPC_INSV: +            check_dsp(ctx); +            { +                TCGv t0, t1; + +                if (rt == 0) { +                    MIPS_DEBUG("NOP"); +                    break; +                } + +                t0 = tcg_temp_new(); +                t1 = tcg_temp_new(); + +                gen_load_gpr(t0, rt); +                gen_load_gpr(t1, rs); + +                gen_helper_insv(cpu_gpr[rt], cpu_env, t1, t0); + +                tcg_temp_free(t0); +                tcg_temp_free(t1); +                break; +            } +        default:            /* Invalid */ +            MIPS_INVAL("MASK INSV"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_APPEND_DSP: +        gen_mipsdsp_append(env, ctx, op1, rt, rs, rd); +        break; +    case OPC_EXTR_W_DSP: +        op2 = MASK_EXTR_W(ctx->opcode); +        switch (op2) { +        case OPC_EXTR_W: +        case OPC_EXTR_R_W: +        case OPC_EXTR_RS_W: +        case OPC_EXTR_S_H: +        case OPC_EXTRV_S_H: +        case OPC_EXTRV_W: +        case OPC_EXTRV_R_W: +        case OPC_EXTRV_RS_W: +        case OPC_EXTP: +        case OPC_EXTPV: +        case OPC_EXTPDP: +        case OPC_EXTPDPV: +            gen_mipsdsp_accinsn(ctx, op1, op2, rt, rs, rd, 1); +            break; +        case OPC_RDDSP: +            gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 1); +            break; +        case OPC_SHILO: +        case OPC_SHILOV: +        case OPC_MTHLIP: +        case OPC_WRDSP: +            gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 0); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK EXTR.W"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +#if defined(TARGET_MIPS64) +    case OPC_DDIV_G_2E ... OPC_DDIVU_G_2E: +    case OPC_DMULT_G_2E ... OPC_DMULTU_G_2E: +    case OPC_DMOD_G_2E ... OPC_DMODU_G_2E: +        check_insn(ctx, INSN_LOONGSON2E); +        gen_loongson_integer(ctx, op1, rd, rs, rt); +        break; +    case OPC_ABSQ_S_QH_DSP: +        op2 = MASK_ABSQ_S_QH(ctx->opcode); +        switch (op2) { +        case OPC_PRECEQ_L_PWL: +        case OPC_PRECEQ_L_PWR: +        case OPC_PRECEQ_PW_QHL: +        case OPC_PRECEQ_PW_QHR: +        case OPC_PRECEQ_PW_QHLA: +        case OPC_PRECEQ_PW_QHRA: +        case OPC_PRECEQU_QH_OBL: +        case OPC_PRECEQU_QH_OBR: +        case OPC_PRECEQU_QH_OBLA: +        case OPC_PRECEQU_QH_OBRA: +        case OPC_PRECEU_QH_OBL: +        case OPC_PRECEU_QH_OBR: +        case OPC_PRECEU_QH_OBLA: +        case OPC_PRECEU_QH_OBRA: +        case OPC_ABSQ_S_OB: +        case OPC_ABSQ_S_PW: +        case OPC_ABSQ_S_QH: +            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); +            break; +        case OPC_REPL_OB: +        case OPC_REPL_PW: +        case OPC_REPL_QH: +        case OPC_REPLV_OB: +        case OPC_REPLV_PW: +        case OPC_REPLV_QH: +            gen_mipsdsp_bitinsn(ctx, op1, op2, rd, rt); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK ABSQ_S.QH"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_ADDU_OB_DSP: +        op2 = MASK_ADDU_OB(ctx->opcode); +        switch (op2) { +        case OPC_RADDU_L_OB: +        case OPC_SUBQ_PW: +        case OPC_SUBQ_S_PW: +        case OPC_SUBQ_QH: +        case OPC_SUBQ_S_QH: +        case OPC_SUBU_OB: +        case OPC_SUBU_S_OB: +        case OPC_SUBU_QH: +        case OPC_SUBU_S_QH: +        case OPC_SUBUH_OB: +        case OPC_SUBUH_R_OB: +        case OPC_ADDQ_PW: +        case OPC_ADDQ_S_PW: +        case OPC_ADDQ_QH: +        case OPC_ADDQ_S_QH: +        case OPC_ADDU_OB: +        case OPC_ADDU_S_OB: +        case OPC_ADDU_QH: +        case OPC_ADDU_S_QH: +        case OPC_ADDUH_OB: +        case OPC_ADDUH_R_OB: +            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); +            break; +        case OPC_MULEQ_S_PW_QHL: +        case OPC_MULEQ_S_PW_QHR: +        case OPC_MULEU_S_QH_OBL: +        case OPC_MULEU_S_QH_OBR: +        case OPC_MULQ_RS_QH: +            gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 1); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK ADDU.OB"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_CMPU_EQ_OB_DSP: +        op2 = MASK_CMPU_EQ_OB(ctx->opcode); +        switch (op2) { +        case OPC_PRECR_SRA_QH_PW: +        case OPC_PRECR_SRA_R_QH_PW: +            /* Return value is rt. */ +            gen_mipsdsp_arith(ctx, op1, op2, rt, rs, rd); +            break; +        case OPC_PRECR_OB_QH: +        case OPC_PRECRQ_OB_QH: +        case OPC_PRECRQ_PW_L: +        case OPC_PRECRQ_QH_PW: +        case OPC_PRECRQ_RS_QH_PW: +        case OPC_PRECRQU_S_OB_QH: +            gen_mipsdsp_arith(ctx, op1, op2, rd, rs, rt); +            break; +        case OPC_CMPU_EQ_OB: +        case OPC_CMPU_LT_OB: +        case OPC_CMPU_LE_OB: +        case OPC_CMP_EQ_QH: +        case OPC_CMP_LT_QH: +        case OPC_CMP_LE_QH: +        case OPC_CMP_EQ_PW: +        case OPC_CMP_LT_PW: +        case OPC_CMP_LE_PW: +            gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 0); +            break; +        case OPC_CMPGDU_EQ_OB: +        case OPC_CMPGDU_LT_OB: +        case OPC_CMPGDU_LE_OB: +        case OPC_CMPGU_EQ_OB: +        case OPC_CMPGU_LT_OB: +        case OPC_CMPGU_LE_OB: +        case OPC_PACKRL_PW: +        case OPC_PICK_OB: +        case OPC_PICK_PW: +        case OPC_PICK_QH: +            gen_mipsdsp_add_cmp_pick(ctx, op1, op2, rd, rs, rt, 1); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK CMPU_EQ.OB"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_DAPPEND_DSP: +        gen_mipsdsp_append(env, ctx, op1, rt, rs, rd); +        break; +    case OPC_DEXTR_W_DSP: +        op2 = MASK_DEXTR_W(ctx->opcode); +        switch (op2) { +        case OPC_DEXTP: +        case OPC_DEXTPDP: +        case OPC_DEXTPDPV: +        case OPC_DEXTPV: +        case OPC_DEXTR_L: +        case OPC_DEXTR_R_L: +        case OPC_DEXTR_RS_L: +        case OPC_DEXTR_W: +        case OPC_DEXTR_R_W: +        case OPC_DEXTR_RS_W: +        case OPC_DEXTR_S_H: +        case OPC_DEXTRV_L: +        case OPC_DEXTRV_R_L: +        case OPC_DEXTRV_RS_L: +        case OPC_DEXTRV_S_H: +        case OPC_DEXTRV_W: +        case OPC_DEXTRV_R_W: +        case OPC_DEXTRV_RS_W: +            gen_mipsdsp_accinsn(ctx, op1, op2, rt, rs, rd, 1); +            break; +        case OPC_DMTHLIP: +        case OPC_DSHILO: +        case OPC_DSHILOV: +            gen_mipsdsp_accinsn(ctx, op1, op2, rd, rs, rt, 0); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK EXTR.W"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_DPAQ_W_QH_DSP: +        op2 = MASK_DPAQ_W_QH(ctx->opcode); +        switch (op2) { +        case OPC_DPAU_H_OBL: +        case OPC_DPAU_H_OBR: +        case OPC_DPSU_H_OBL: +        case OPC_DPSU_H_OBR: +        case OPC_DPA_W_QH: +        case OPC_DPAQ_S_W_QH: +        case OPC_DPS_W_QH: +        case OPC_DPSQ_S_W_QH: +        case OPC_MULSAQ_S_W_QH: +        case OPC_DPAQ_SA_L_PW: +        case OPC_DPSQ_SA_L_PW: +        case OPC_MULSAQ_S_L_PW: +            gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0); +            break; +        case OPC_MAQ_S_W_QHLL: +        case OPC_MAQ_S_W_QHLR: +        case OPC_MAQ_S_W_QHRL: +        case OPC_MAQ_S_W_QHRR: +        case OPC_MAQ_SA_W_QHLL: +        case OPC_MAQ_SA_W_QHLR: +        case OPC_MAQ_SA_W_QHRL: +        case OPC_MAQ_SA_W_QHRR: +        case OPC_MAQ_S_L_PWL: +        case OPC_MAQ_S_L_PWR: +        case OPC_DMADD: +        case OPC_DMADDU: +        case OPC_DMSUB: +        case OPC_DMSUBU: +            gen_mipsdsp_multiply(ctx, op1, op2, rd, rs, rt, 0); +            break; +        default:            /* Invalid */ +            MIPS_INVAL("MASK DPAQ.W.QH"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_DINSV_DSP: +        op2 = MASK_INSV(ctx->opcode); +        switch (op2) { +        case OPC_DINSV: +        { +            TCGv t0, t1; + +            if (rt == 0) { +                MIPS_DEBUG("NOP"); +                break; +            } +            check_dsp(ctx); + +            t0 = tcg_temp_new(); +            t1 = tcg_temp_new(); + +            gen_load_gpr(t0, rt); +            gen_load_gpr(t1, rs); + +            gen_helper_dinsv(cpu_gpr[rt], cpu_env, t1, t0); + +            tcg_temp_free(t0); +            tcg_temp_free(t1); +            break; +        } +        default:            /* Invalid */ +            MIPS_INVAL("MASK DINSV"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_SHLL_OB_DSP: +        gen_mipsdsp_shift(ctx, op1, rd, rs, rt); +        break; +#endif +    default:            /* Invalid */ +        MIPS_INVAL("special3_legacy"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) +{ +    int rs, rt, rd, sa; +    uint32_t op1, op2; + +    rs = (ctx->opcode >> 21) & 0x1f; +    rt = (ctx->opcode >> 16) & 0x1f; +    rd = (ctx->opcode >> 11) & 0x1f; +    sa = (ctx->opcode >> 6) & 0x1f; + +    op1 = MASK_SPECIAL3(ctx->opcode); +    switch (op1) { +    case OPC_EXT: +    case OPC_INS: +        check_insn(ctx, ISA_MIPS32R2); +        gen_bitops(ctx, op1, rt, rs, sa, rd); +        break; +    case OPC_BSHFL: +        op2 = MASK_BSHFL(ctx->opcode); +        switch (op2) { +        case OPC_ALIGN ... OPC_ALIGN_END: +        case OPC_BITSWAP: +            check_insn(ctx, ISA_MIPS32R6); +            decode_opc_special3_r6(env, ctx); +            break; +        default: +            check_insn(ctx, ISA_MIPS32R2); +            gen_bshfl(ctx, op2, rt, rd); +            break; +        } +        break; +#if defined(TARGET_MIPS64) +    case OPC_DEXTM ... OPC_DEXT: +    case OPC_DINSM ... OPC_DINS: +        check_insn(ctx, ISA_MIPS64R2); +        check_mips_64(ctx); +        gen_bitops(ctx, op1, rt, rs, sa, rd); +        break; +    case OPC_DBSHFL: +        op2 = MASK_DBSHFL(ctx->opcode); +        switch (op2) { +        case OPC_DALIGN ... OPC_DALIGN_END: +        case OPC_DBITSWAP: +            check_insn(ctx, ISA_MIPS32R6); +            decode_opc_special3_r6(env, ctx); +            break; +        default: +            check_insn(ctx, ISA_MIPS64R2); +            check_mips_64(ctx); +            op2 = MASK_DBSHFL(ctx->opcode); +            gen_bshfl(ctx, op2, rt, rd); +            break; +        } +        break; +#endif +    case OPC_RDHWR: +        gen_rdhwr(ctx, rt, rd); +        break; +    case OPC_FORK: +        check_insn(ctx, ASE_MT); +        { +            TCGv t0 = tcg_temp_new(); +            TCGv t1 = tcg_temp_new(); + +            gen_load_gpr(t0, rt); +            gen_load_gpr(t1, rs); +            gen_helper_fork(t0, t1); +            tcg_temp_free(t0); +            tcg_temp_free(t1); +        } +        break; +    case OPC_YIELD: +        check_insn(ctx, ASE_MT); +        { +            TCGv t0 = tcg_temp_new(); + +            save_cpu_state(ctx, 1); +            gen_load_gpr(t0, rs); +            gen_helper_yield(t0, cpu_env, t0); +            gen_store_gpr(t0, rd); +            tcg_temp_free(t0); +        } +        break; +    default: +        if (ctx->insn_flags & ISA_MIPS32R6) { +            decode_opc_special3_r6(env, ctx); +        } else { +            decode_opc_special3_legacy(env, ctx); +        } +    } +} + +/* MIPS SIMD Architecture (MSA)  */ +static inline int check_msa_access(DisasContext *ctx) +{ +    if (unlikely((ctx->hflags & MIPS_HFLAG_FPU) && +                 !(ctx->hflags & MIPS_HFLAG_F64))) { +        generate_exception(ctx, EXCP_RI); +        return 0; +    } + +    if (unlikely(!(ctx->hflags & MIPS_HFLAG_MSA))) { +        if (ctx->insn_flags & ASE_MSA) { +            generate_exception(ctx, EXCP_MSADIS); +            return 0; +        } else { +            generate_exception(ctx, EXCP_RI); +            return 0; +        } +    } +    return 1; +} + +static void gen_check_zero_element(TCGv tresult, uint8_t df, uint8_t wt) +{ +    /* generates tcg ops to check if any element is 0 */ +    /* Note this function only works with MSA_WRLEN = 128 */ +    uint64_t eval_zero_or_big = 0; +    uint64_t eval_big = 0; +    TCGv_i64 t0 = tcg_temp_new_i64(); +    TCGv_i64 t1 = tcg_temp_new_i64(); +    switch (df) { +    case DF_BYTE: +        eval_zero_or_big = 0x0101010101010101ULL; +        eval_big = 0x8080808080808080ULL; +        break; +    case DF_HALF: +        eval_zero_or_big = 0x0001000100010001ULL; +        eval_big = 0x8000800080008000ULL; +        break; +    case DF_WORD: +        eval_zero_or_big = 0x0000000100000001ULL; +        eval_big = 0x8000000080000000ULL; +        break; +    case DF_DOUBLE: +        eval_zero_or_big = 0x0000000000000001ULL; +        eval_big = 0x8000000000000000ULL; +        break; +    } +    tcg_gen_subi_i64(t0, msa_wr_d[wt<<1], eval_zero_or_big); +    tcg_gen_andc_i64(t0, t0, msa_wr_d[wt<<1]); +    tcg_gen_andi_i64(t0, t0, eval_big); +    tcg_gen_subi_i64(t1, msa_wr_d[(wt<<1)+1], eval_zero_or_big); +    tcg_gen_andc_i64(t1, t1, msa_wr_d[(wt<<1)+1]); +    tcg_gen_andi_i64(t1, t1, eval_big); +    tcg_gen_or_i64(t0, t0, t1); +    /* if all bits are zero then all elements are not zero */ +    /* if some bit is non-zero then some element is zero */ +    tcg_gen_setcondi_i64(TCG_COND_NE, t0, t0, 0); +    tcg_gen_trunc_i64_tl(tresult, t0); +    tcg_temp_free_i64(t0); +    tcg_temp_free_i64(t1); +} + +static void gen_msa_branch(CPUMIPSState *env, DisasContext *ctx, uint32_t op1) +{ +    uint8_t df = (ctx->opcode >> 21) & 0x3; +    uint8_t wt = (ctx->opcode >> 16) & 0x1f; +    int64_t s16 = (int16_t)ctx->opcode; + +    check_msa_access(ctx); + +    if (ctx->insn_flags & ISA_MIPS32R6 && ctx->hflags & MIPS_HFLAG_BMASK) { +        MIPS_DEBUG("CTI in delay / forbidden slot"); +        generate_exception(ctx, EXCP_RI); +        return; +    } +    switch (op1) { +    case OPC_BZ_V: +    case OPC_BNZ_V: +        { +            TCGv_i64 t0 = tcg_temp_new_i64(); +            tcg_gen_or_i64(t0, msa_wr_d[wt<<1], msa_wr_d[(wt<<1)+1]); +            tcg_gen_setcondi_i64((op1 == OPC_BZ_V) ? +                    TCG_COND_EQ : TCG_COND_NE, t0, t0, 0); +            tcg_gen_trunc_i64_tl(bcond, t0); +            tcg_temp_free_i64(t0); +        } +        break; +    case OPC_BZ_B: +    case OPC_BZ_H: +    case OPC_BZ_W: +    case OPC_BZ_D: +        gen_check_zero_element(bcond, df, wt); +        break; +    case OPC_BNZ_B: +    case OPC_BNZ_H: +    case OPC_BNZ_W: +    case OPC_BNZ_D: +        gen_check_zero_element(bcond, df, wt); +        tcg_gen_setcondi_tl(TCG_COND_EQ, bcond, bcond, 0); +        break; +    } + +    ctx->btarget = ctx->pc + (s16 << 2) + 4; + +    ctx->hflags |= MIPS_HFLAG_BC; +    ctx->hflags |= MIPS_HFLAG_BDS32; +} + +static void gen_msa_i8(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_I8(op)    (MASK_MSA_MINOR(op) | (op & (0x03 << 24))) +    uint8_t i8 = (ctx->opcode >> 16) & 0xff; +    uint8_t ws = (ctx->opcode >> 11) & 0x1f; +    uint8_t wd = (ctx->opcode >> 6) & 0x1f; + +    TCGv_i32 twd = tcg_const_i32(wd); +    TCGv_i32 tws = tcg_const_i32(ws); +    TCGv_i32 ti8 = tcg_const_i32(i8); + +    switch (MASK_MSA_I8(ctx->opcode)) { +    case OPC_ANDI_B: +        gen_helper_msa_andi_b(cpu_env, twd, tws, ti8); +        break; +    case OPC_ORI_B: +        gen_helper_msa_ori_b(cpu_env, twd, tws, ti8); +        break; +    case OPC_NORI_B: +        gen_helper_msa_nori_b(cpu_env, twd, tws, ti8); +        break; +    case OPC_XORI_B: +        gen_helper_msa_xori_b(cpu_env, twd, tws, ti8); +        break; +    case OPC_BMNZI_B: +        gen_helper_msa_bmnzi_b(cpu_env, twd, tws, ti8); +        break; +    case OPC_BMZI_B: +        gen_helper_msa_bmzi_b(cpu_env, twd, tws, ti8); +        break; +    case OPC_BSELI_B: +        gen_helper_msa_bseli_b(cpu_env, twd, tws, ti8); +        break; +    case OPC_SHF_B: +    case OPC_SHF_H: +    case OPC_SHF_W: +        { +            uint8_t df = (ctx->opcode >> 24) & 0x3; +            if (df == DF_DOUBLE) { +                generate_exception(ctx, EXCP_RI); +            } else { +                TCGv_i32 tdf = tcg_const_i32(df); +                gen_helper_msa_shf_df(cpu_env, tdf, twd, tws, ti8); +                tcg_temp_free_i32(tdf); +            } +        } +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +        break; +    } + +    tcg_temp_free_i32(twd); +    tcg_temp_free_i32(tws); +    tcg_temp_free_i32(ti8); +} + +static void gen_msa_i5(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_I5(op)    (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) +    uint8_t df = (ctx->opcode >> 21) & 0x3; +    int8_t s5 = (int8_t) sextract32(ctx->opcode, 16, 5); +    uint8_t u5 = (ctx->opcode >> 16) & 0x1f; +    uint8_t ws = (ctx->opcode >> 11) & 0x1f; +    uint8_t wd = (ctx->opcode >> 6) & 0x1f; + +    TCGv_i32 tdf = tcg_const_i32(df); +    TCGv_i32 twd = tcg_const_i32(wd); +    TCGv_i32 tws = tcg_const_i32(ws); +    TCGv_i32 timm = tcg_temp_new_i32(); +    tcg_gen_movi_i32(timm, u5); + +    switch (MASK_MSA_I5(ctx->opcode)) { +    case OPC_ADDVI_df: +        gen_helper_msa_addvi_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_SUBVI_df: +        gen_helper_msa_subvi_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_MAXI_S_df: +        tcg_gen_movi_i32(timm, s5); +        gen_helper_msa_maxi_s_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_MAXI_U_df: +        gen_helper_msa_maxi_u_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_MINI_S_df: +        tcg_gen_movi_i32(timm, s5); +        gen_helper_msa_mini_s_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_MINI_U_df: +        gen_helper_msa_mini_u_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_CEQI_df: +        tcg_gen_movi_i32(timm, s5); +        gen_helper_msa_ceqi_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_CLTI_S_df: +        tcg_gen_movi_i32(timm, s5); +        gen_helper_msa_clti_s_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_CLTI_U_df: +        gen_helper_msa_clti_u_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_CLEI_S_df: +        tcg_gen_movi_i32(timm, s5); +        gen_helper_msa_clei_s_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_CLEI_U_df: +        gen_helper_msa_clei_u_df(cpu_env, tdf, twd, tws, timm); +        break; +    case OPC_LDI_df: +        { +            int32_t s10 = sextract32(ctx->opcode, 11, 10); +            tcg_gen_movi_i32(timm, s10); +            gen_helper_msa_ldi_df(cpu_env, tdf, twd, timm); +        } +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +        break; +    } + +    tcg_temp_free_i32(tdf); +    tcg_temp_free_i32(twd); +    tcg_temp_free_i32(tws); +    tcg_temp_free_i32(timm); +} + +static void gen_msa_bit(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_BIT(op)    (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) +    uint8_t dfm = (ctx->opcode >> 16) & 0x7f; +    uint32_t df = 0, m = 0; +    uint8_t ws = (ctx->opcode >> 11) & 0x1f; +    uint8_t wd = (ctx->opcode >> 6) & 0x1f; + +    TCGv_i32 tdf; +    TCGv_i32 tm; +    TCGv_i32 twd; +    TCGv_i32 tws; + +    if ((dfm & 0x40) == 0x00) { +        m = dfm & 0x3f; +        df = DF_DOUBLE; +    } else if ((dfm & 0x60) == 0x40) { +        m = dfm & 0x1f; +        df = DF_WORD; +    } else if ((dfm & 0x70) == 0x60) { +        m = dfm & 0x0f; +        df = DF_HALF; +    } else if ((dfm & 0x78) == 0x70) { +        m = dfm & 0x7; +        df = DF_BYTE; +    } else { +        generate_exception(ctx, EXCP_RI); +        return; +    } + +    tdf = tcg_const_i32(df); +    tm  = tcg_const_i32(m); +    twd = tcg_const_i32(wd); +    tws = tcg_const_i32(ws); + +    switch (MASK_MSA_BIT(ctx->opcode)) { +    case OPC_SLLI_df: +        gen_helper_msa_slli_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_SRAI_df: +        gen_helper_msa_srai_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_SRLI_df: +        gen_helper_msa_srli_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_BCLRI_df: +        gen_helper_msa_bclri_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_BSETI_df: +        gen_helper_msa_bseti_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_BNEGI_df: +        gen_helper_msa_bnegi_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_BINSLI_df: +        gen_helper_msa_binsli_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_BINSRI_df: +        gen_helper_msa_binsri_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_SAT_S_df: +        gen_helper_msa_sat_s_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_SAT_U_df: +        gen_helper_msa_sat_u_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_SRARI_df: +        gen_helper_msa_srari_df(cpu_env, tdf, twd, tws, tm); +        break; +    case OPC_SRLRI_df: +        gen_helper_msa_srlri_df(cpu_env, tdf, twd, tws, tm); +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +        break; +    } + +    tcg_temp_free_i32(tdf); +    tcg_temp_free_i32(tm); +    tcg_temp_free_i32(twd); +    tcg_temp_free_i32(tws); +} + +static void gen_msa_3r(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_3R(op)    (MASK_MSA_MINOR(op) | (op & (0x7 << 23))) +    uint8_t df = (ctx->opcode >> 21) & 0x3; +    uint8_t wt = (ctx->opcode >> 16) & 0x1f; +    uint8_t ws = (ctx->opcode >> 11) & 0x1f; +    uint8_t wd = (ctx->opcode >> 6) & 0x1f; + +    TCGv_i32 tdf = tcg_const_i32(df); +    TCGv_i32 twd = tcg_const_i32(wd); +    TCGv_i32 tws = tcg_const_i32(ws); +    TCGv_i32 twt = tcg_const_i32(wt); + +    switch (MASK_MSA_3R(ctx->opcode)) { +    case OPC_SLL_df: +        gen_helper_msa_sll_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ADDV_df: +        gen_helper_msa_addv_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_CEQ_df: +        gen_helper_msa_ceq_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ADD_A_df: +        gen_helper_msa_add_a_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SUBS_S_df: +        gen_helper_msa_subs_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MULV_df: +        gen_helper_msa_mulv_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SLD_df: +        gen_helper_msa_sld_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_VSHF_df: +        gen_helper_msa_vshf_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SRA_df: +        gen_helper_msa_sra_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SUBV_df: +        gen_helper_msa_subv_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ADDS_A_df: +        gen_helper_msa_adds_a_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SUBS_U_df: +        gen_helper_msa_subs_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MADDV_df: +        gen_helper_msa_maddv_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SPLAT_df: +        gen_helper_msa_splat_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SRAR_df: +        gen_helper_msa_srar_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SRL_df: +        gen_helper_msa_srl_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MAX_S_df: +        gen_helper_msa_max_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_CLT_S_df: +        gen_helper_msa_clt_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ADDS_S_df: +        gen_helper_msa_adds_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SUBSUS_U_df: +        gen_helper_msa_subsus_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MSUBV_df: +        gen_helper_msa_msubv_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_PCKEV_df: +        gen_helper_msa_pckev_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SRLR_df: +        gen_helper_msa_srlr_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_BCLR_df: +        gen_helper_msa_bclr_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MAX_U_df: +        gen_helper_msa_max_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_CLT_U_df: +        gen_helper_msa_clt_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ADDS_U_df: +        gen_helper_msa_adds_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_SUBSUU_S_df: +        gen_helper_msa_subsuu_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_PCKOD_df: +        gen_helper_msa_pckod_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_BSET_df: +        gen_helper_msa_bset_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MIN_S_df: +        gen_helper_msa_min_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_CLE_S_df: +        gen_helper_msa_cle_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_AVE_S_df: +        gen_helper_msa_ave_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ASUB_S_df: +        gen_helper_msa_asub_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_DIV_S_df: +        gen_helper_msa_div_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ILVL_df: +        gen_helper_msa_ilvl_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_BNEG_df: +        gen_helper_msa_bneg_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MIN_U_df: +        gen_helper_msa_min_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_CLE_U_df: +        gen_helper_msa_cle_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_AVE_U_df: +        gen_helper_msa_ave_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ASUB_U_df: +        gen_helper_msa_asub_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_DIV_U_df: +        gen_helper_msa_div_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ILVR_df: +        gen_helper_msa_ilvr_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_BINSL_df: +        gen_helper_msa_binsl_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MAX_A_df: +        gen_helper_msa_max_a_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_AVER_S_df: +        gen_helper_msa_aver_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MOD_S_df: +        gen_helper_msa_mod_s_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ILVEV_df: +        gen_helper_msa_ilvev_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_BINSR_df: +        gen_helper_msa_binsr_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MIN_A_df: +        gen_helper_msa_min_a_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_AVER_U_df: +        gen_helper_msa_aver_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MOD_U_df: +        gen_helper_msa_mod_u_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_ILVOD_df: +        gen_helper_msa_ilvod_df(cpu_env, tdf, twd, tws, twt); +        break; + +    case OPC_DOTP_S_df: +    case OPC_DOTP_U_df: +    case OPC_DPADD_S_df: +    case OPC_DPADD_U_df: +    case OPC_DPSUB_S_df: +    case OPC_HADD_S_df: +    case OPC_DPSUB_U_df: +    case OPC_HADD_U_df: +    case OPC_HSUB_S_df: +    case OPC_HSUB_U_df: +        if (df == DF_BYTE) { +            generate_exception(ctx, EXCP_RI); +        } +        switch (MASK_MSA_3R(ctx->opcode)) { +        case OPC_DOTP_S_df: +            gen_helper_msa_dotp_s_df(cpu_env, tdf, twd, tws, twt); +            break; +        case OPC_DOTP_U_df: +            gen_helper_msa_dotp_u_df(cpu_env, tdf, twd, tws, twt); +            break; +        case OPC_DPADD_S_df: +            gen_helper_msa_dpadd_s_df(cpu_env, tdf, twd, tws, twt); +            break; +        case OPC_DPADD_U_df: +            gen_helper_msa_dpadd_u_df(cpu_env, tdf, twd, tws, twt); +            break; +        case OPC_DPSUB_S_df: +            gen_helper_msa_dpsub_s_df(cpu_env, tdf, twd, tws, twt); +            break; +        case OPC_HADD_S_df: +            gen_helper_msa_hadd_s_df(cpu_env, tdf, twd, tws, twt); +            break; +        case OPC_DPSUB_U_df: +            gen_helper_msa_dpsub_u_df(cpu_env, tdf, twd, tws, twt); +            break; +        case OPC_HADD_U_df: +            gen_helper_msa_hadd_u_df(cpu_env, tdf, twd, tws, twt); +            break; +        case OPC_HSUB_S_df: +            gen_helper_msa_hsub_s_df(cpu_env, tdf, twd, tws, twt); +            break; +        case OPC_HSUB_U_df: +            gen_helper_msa_hsub_u_df(cpu_env, tdf, twd, tws, twt); +            break; +        } +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +    tcg_temp_free_i32(twd); +    tcg_temp_free_i32(tws); +    tcg_temp_free_i32(twt); +    tcg_temp_free_i32(tdf); +} + +static void gen_msa_elm_3e(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_ELM_DF3E(op)   (MASK_MSA_MINOR(op) | (op & (0x3FF << 16))) +    uint8_t source = (ctx->opcode >> 11) & 0x1f; +    uint8_t dest = (ctx->opcode >> 6) & 0x1f; +    TCGv telm = tcg_temp_new(); +    TCGv_i32 tsr = tcg_const_i32(source); +    TCGv_i32 tdt = tcg_const_i32(dest); + +    switch (MASK_MSA_ELM_DF3E(ctx->opcode)) { +    case OPC_CTCMSA: +        gen_load_gpr(telm, source); +        gen_helper_msa_ctcmsa(cpu_env, telm, tdt); +        break; +    case OPC_CFCMSA: +        gen_helper_msa_cfcmsa(telm, cpu_env, tsr); +        gen_store_gpr(telm, dest); +        break; +    case OPC_MOVE_V: +        gen_helper_msa_move_v(cpu_env, tdt, tsr); +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +        break; +    } + +    tcg_temp_free(telm); +    tcg_temp_free_i32(tdt); +    tcg_temp_free_i32(tsr); +} + +static void gen_msa_elm_df(CPUMIPSState *env, DisasContext *ctx, uint32_t df, +        uint32_t n) +{ +#define MASK_MSA_ELM(op)    (MASK_MSA_MINOR(op) | (op & (0xf << 22))) +    uint8_t ws = (ctx->opcode >> 11) & 0x1f; +    uint8_t wd = (ctx->opcode >> 6) & 0x1f; + +    TCGv_i32 tws = tcg_const_i32(ws); +    TCGv_i32 twd = tcg_const_i32(wd); +    TCGv_i32 tn  = tcg_const_i32(n); +    TCGv_i32 tdf = tcg_const_i32(df); + +    switch (MASK_MSA_ELM(ctx->opcode)) { +    case OPC_SLDI_df: +        gen_helper_msa_sldi_df(cpu_env, tdf, twd, tws, tn); +        break; +    case OPC_SPLATI_df: +        gen_helper_msa_splati_df(cpu_env, tdf, twd, tws, tn); +        break; +    case OPC_INSVE_df: +        gen_helper_msa_insve_df(cpu_env, tdf, twd, tws, tn); +        break; +    case OPC_COPY_S_df: +    case OPC_COPY_U_df: +    case OPC_INSERT_df: +#if !defined(TARGET_MIPS64) +        /* Double format valid only for MIPS64 */ +        if (df == DF_DOUBLE) { +            generate_exception(ctx, EXCP_RI); +            break; +        } +#endif +        switch (MASK_MSA_ELM(ctx->opcode)) { +        case OPC_COPY_S_df: +            gen_helper_msa_copy_s_df(cpu_env, tdf, twd, tws, tn); +            break; +        case OPC_COPY_U_df: +            gen_helper_msa_copy_u_df(cpu_env, tdf, twd, tws, tn); +            break; +        case OPC_INSERT_df: +            gen_helper_msa_insert_df(cpu_env, tdf, twd, tws, tn); +            break; +        } +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +    } +    tcg_temp_free_i32(twd); +    tcg_temp_free_i32(tws); +    tcg_temp_free_i32(tn); +    tcg_temp_free_i32(tdf); +} + +static void gen_msa_elm(CPUMIPSState *env, DisasContext *ctx) +{ +    uint8_t dfn = (ctx->opcode >> 16) & 0x3f; +    uint32_t df = 0, n = 0; + +    if ((dfn & 0x30) == 0x00) { +        n = dfn & 0x0f; +        df = DF_BYTE; +    } else if ((dfn & 0x38) == 0x20) { +        n = dfn & 0x07; +        df = DF_HALF; +    } else if ((dfn & 0x3c) == 0x30) { +        n = dfn & 0x03; +        df = DF_WORD; +    } else if ((dfn & 0x3e) == 0x38) { +        n = dfn & 0x01; +        df = DF_DOUBLE; +    } else if (dfn == 0x3E) { +        /* CTCMSA, CFCMSA, MOVE.V */ +        gen_msa_elm_3e(env, ctx); +        return; +    } else { +        generate_exception(ctx, EXCP_RI); +        return; +    } + +    gen_msa_elm_df(env, ctx, df, n); +} + +static void gen_msa_3rf(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_3RF(op)    (MASK_MSA_MINOR(op) | (op & (0xf << 22))) +    uint8_t df = (ctx->opcode >> 21) & 0x1; +    uint8_t wt = (ctx->opcode >> 16) & 0x1f; +    uint8_t ws = (ctx->opcode >> 11) & 0x1f; +    uint8_t wd = (ctx->opcode >> 6) & 0x1f; + +    TCGv_i32 twd = tcg_const_i32(wd); +    TCGv_i32 tws = tcg_const_i32(ws); +    TCGv_i32 twt = tcg_const_i32(wt); +    TCGv_i32 tdf = tcg_temp_new_i32(); + +    /* adjust df value for floating-point instruction */ +    tcg_gen_movi_i32(tdf, df + 2); + +    switch (MASK_MSA_3RF(ctx->opcode)) { +    case OPC_FCAF_df: +        gen_helper_msa_fcaf_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FADD_df: +        gen_helper_msa_fadd_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FCUN_df: +        gen_helper_msa_fcun_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSUB_df: +        gen_helper_msa_fsub_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FCOR_df: +        gen_helper_msa_fcor_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FCEQ_df: +        gen_helper_msa_fceq_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FMUL_df: +        gen_helper_msa_fmul_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FCUNE_df: +        gen_helper_msa_fcune_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FCUEQ_df: +        gen_helper_msa_fcueq_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FDIV_df: +        gen_helper_msa_fdiv_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FCNE_df: +        gen_helper_msa_fcne_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FCLT_df: +        gen_helper_msa_fclt_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FMADD_df: +        gen_helper_msa_fmadd_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MUL_Q_df: +        tcg_gen_movi_i32(tdf, df + 1); +        gen_helper_msa_mul_q_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FCULT_df: +        gen_helper_msa_fcult_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FMSUB_df: +        gen_helper_msa_fmsub_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MADD_Q_df: +        tcg_gen_movi_i32(tdf, df + 1); +        gen_helper_msa_madd_q_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FCLE_df: +        gen_helper_msa_fcle_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MSUB_Q_df: +        tcg_gen_movi_i32(tdf, df + 1); +        gen_helper_msa_msub_q_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FCULE_df: +        gen_helper_msa_fcule_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FEXP2_df: +        gen_helper_msa_fexp2_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSAF_df: +        gen_helper_msa_fsaf_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FEXDO_df: +        gen_helper_msa_fexdo_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSUN_df: +        gen_helper_msa_fsun_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSOR_df: +        gen_helper_msa_fsor_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSEQ_df: +        gen_helper_msa_fseq_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FTQ_df: +        gen_helper_msa_ftq_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSUNE_df: +        gen_helper_msa_fsune_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSUEQ_df: +        gen_helper_msa_fsueq_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSNE_df: +        gen_helper_msa_fsne_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSLT_df: +        gen_helper_msa_fslt_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FMIN_df: +        gen_helper_msa_fmin_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MULR_Q_df: +        tcg_gen_movi_i32(tdf, df + 1); +        gen_helper_msa_mulr_q_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSULT_df: +        gen_helper_msa_fsult_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FMIN_A_df: +        gen_helper_msa_fmin_a_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MADDR_Q_df: +        tcg_gen_movi_i32(tdf, df + 1); +        gen_helper_msa_maddr_q_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSLE_df: +        gen_helper_msa_fsle_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FMAX_df: +        gen_helper_msa_fmax_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_MSUBR_Q_df: +        tcg_gen_movi_i32(tdf, df + 1); +        gen_helper_msa_msubr_q_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FSULE_df: +        gen_helper_msa_fsule_df(cpu_env, tdf, twd, tws, twt); +        break; +    case OPC_FMAX_A_df: +        gen_helper_msa_fmax_a_df(cpu_env, tdf, twd, tws, twt); +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +        break; +    } + +    tcg_temp_free_i32(twd); +    tcg_temp_free_i32(tws); +    tcg_temp_free_i32(twt); +    tcg_temp_free_i32(tdf); +} + +static void gen_msa_2r(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_2R(op)     (MASK_MSA_MINOR(op) | (op & (0x1f << 21)) | \ +                            (op & (0x7 << 18))) +    uint8_t wt = (ctx->opcode >> 16) & 0x1f; +    uint8_t ws = (ctx->opcode >> 11) & 0x1f; +    uint8_t wd = (ctx->opcode >> 6) & 0x1f; +    uint8_t df = (ctx->opcode >> 16) & 0x3; +    TCGv_i32 twd = tcg_const_i32(wd); +    TCGv_i32 tws = tcg_const_i32(ws); +    TCGv_i32 twt = tcg_const_i32(wt); +    TCGv_i32 tdf = tcg_const_i32(df); + +    switch (MASK_MSA_2R(ctx->opcode)) { +    case OPC_FILL_df: +#if !defined(TARGET_MIPS64) +        /* Double format valid only for MIPS64 */ +        if (df == DF_DOUBLE) { +            generate_exception(ctx, EXCP_RI); +            break; +        } +#endif +        gen_helper_msa_fill_df(cpu_env, tdf, twd, tws); /* trs */ +        break; +    case OPC_PCNT_df: +        gen_helper_msa_pcnt_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_NLOC_df: +        gen_helper_msa_nloc_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_NLZC_df: +        gen_helper_msa_nlzc_df(cpu_env, tdf, twd, tws); +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +        break; +    } + +    tcg_temp_free_i32(twd); +    tcg_temp_free_i32(tws); +    tcg_temp_free_i32(twt); +    tcg_temp_free_i32(tdf); +} + +static void gen_msa_2rf(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_2RF(op)    (MASK_MSA_MINOR(op) | (op & (0x1f << 21)) | \ +                            (op & (0xf << 17))) +    uint8_t wt = (ctx->opcode >> 16) & 0x1f; +    uint8_t ws = (ctx->opcode >> 11) & 0x1f; +    uint8_t wd = (ctx->opcode >> 6) & 0x1f; +    uint8_t df = (ctx->opcode >> 16) & 0x1; +    TCGv_i32 twd = tcg_const_i32(wd); +    TCGv_i32 tws = tcg_const_i32(ws); +    TCGv_i32 twt = tcg_const_i32(wt); +    /* adjust df value for floating-point instruction */ +    TCGv_i32 tdf = tcg_const_i32(df + 2); + +    switch (MASK_MSA_2RF(ctx->opcode)) { +    case OPC_FCLASS_df: +        gen_helper_msa_fclass_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FTRUNC_S_df: +        gen_helper_msa_ftrunc_s_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FTRUNC_U_df: +        gen_helper_msa_ftrunc_u_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FSQRT_df: +        gen_helper_msa_fsqrt_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FRSQRT_df: +        gen_helper_msa_frsqrt_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FRCP_df: +        gen_helper_msa_frcp_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FRINT_df: +        gen_helper_msa_frint_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FLOG2_df: +        gen_helper_msa_flog2_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FEXUPL_df: +        gen_helper_msa_fexupl_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FEXUPR_df: +        gen_helper_msa_fexupr_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FFQL_df: +        gen_helper_msa_ffql_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FFQR_df: +        gen_helper_msa_ffqr_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FTINT_S_df: +        gen_helper_msa_ftint_s_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FTINT_U_df: +        gen_helper_msa_ftint_u_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FFINT_S_df: +        gen_helper_msa_ffint_s_df(cpu_env, tdf, twd, tws); +        break; +    case OPC_FFINT_U_df: +        gen_helper_msa_ffint_u_df(cpu_env, tdf, twd, tws); +        break; +    } + +    tcg_temp_free_i32(twd); +    tcg_temp_free_i32(tws); +    tcg_temp_free_i32(twt); +    tcg_temp_free_i32(tdf); +} + +static void gen_msa_vec_v(CPUMIPSState *env, DisasContext *ctx) +{ +#define MASK_MSA_VEC(op)    (MASK_MSA_MINOR(op) | (op & (0x1f << 21))) +    uint8_t wt = (ctx->opcode >> 16) & 0x1f; +    uint8_t ws = (ctx->opcode >> 11) & 0x1f; +    uint8_t wd = (ctx->opcode >> 6) & 0x1f; +    TCGv_i32 twd = tcg_const_i32(wd); +    TCGv_i32 tws = tcg_const_i32(ws); +    TCGv_i32 twt = tcg_const_i32(wt); + +    switch (MASK_MSA_VEC(ctx->opcode)) { +    case OPC_AND_V: +        gen_helper_msa_and_v(cpu_env, twd, tws, twt); +        break; +    case OPC_OR_V: +        gen_helper_msa_or_v(cpu_env, twd, tws, twt); +        break; +    case OPC_NOR_V: +        gen_helper_msa_nor_v(cpu_env, twd, tws, twt); +        break; +    case OPC_XOR_V: +        gen_helper_msa_xor_v(cpu_env, twd, tws, twt); +        break; +    case OPC_BMNZ_V: +        gen_helper_msa_bmnz_v(cpu_env, twd, tws, twt); +        break; +    case OPC_BMZ_V: +        gen_helper_msa_bmz_v(cpu_env, twd, tws, twt); +        break; +    case OPC_BSEL_V: +        gen_helper_msa_bsel_v(cpu_env, twd, tws, twt); +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +        break; +    } + +    tcg_temp_free_i32(twd); +    tcg_temp_free_i32(tws); +    tcg_temp_free_i32(twt); +} + +static void gen_msa_vec(CPUMIPSState *env, DisasContext *ctx) +{ +    switch (MASK_MSA_VEC(ctx->opcode)) { +    case OPC_AND_V: +    case OPC_OR_V: +    case OPC_NOR_V: +    case OPC_XOR_V: +    case OPC_BMNZ_V: +    case OPC_BMZ_V: +    case OPC_BSEL_V: +        gen_msa_vec_v(env, ctx); +        break; +    case OPC_MSA_2R: +        gen_msa_2r(env, ctx); +        break; +    case OPC_MSA_2RF: +        gen_msa_2rf(env, ctx); +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static void gen_msa(CPUMIPSState *env, DisasContext *ctx) +{ +    uint32_t opcode = ctx->opcode; +    check_insn(ctx, ASE_MSA); +    check_msa_access(ctx); + +    switch (MASK_MSA_MINOR(opcode)) { +    case OPC_MSA_I8_00: +    case OPC_MSA_I8_01: +    case OPC_MSA_I8_02: +        gen_msa_i8(env, ctx); +        break; +    case OPC_MSA_I5_06: +    case OPC_MSA_I5_07: +        gen_msa_i5(env, ctx); +        break; +    case OPC_MSA_BIT_09: +    case OPC_MSA_BIT_0A: +        gen_msa_bit(env, ctx); +        break; +    case OPC_MSA_3R_0D: +    case OPC_MSA_3R_0E: +    case OPC_MSA_3R_0F: +    case OPC_MSA_3R_10: +    case OPC_MSA_3R_11: +    case OPC_MSA_3R_12: +    case OPC_MSA_3R_13: +    case OPC_MSA_3R_14: +    case OPC_MSA_3R_15: +        gen_msa_3r(env, ctx); +        break; +    case OPC_MSA_ELM: +        gen_msa_elm(env, ctx); +        break; +    case OPC_MSA_3RF_1A: +    case OPC_MSA_3RF_1B: +    case OPC_MSA_3RF_1C: +        gen_msa_3rf(env, ctx); +        break; +    case OPC_MSA_VEC: +        gen_msa_vec(env, ctx); +        break; +    case OPC_LD_B: +    case OPC_LD_H: +    case OPC_LD_W: +    case OPC_LD_D: +    case OPC_ST_B: +    case OPC_ST_H: +    case OPC_ST_W: +    case OPC_ST_D: +        { +            int32_t s10 = sextract32(ctx->opcode, 16, 10); +            uint8_t rs = (ctx->opcode >> 11) & 0x1f; +            uint8_t wd = (ctx->opcode >> 6) & 0x1f; +            uint8_t df = (ctx->opcode >> 0) & 0x3; + +            TCGv_i32 twd = tcg_const_i32(wd); +            TCGv taddr = tcg_temp_new(); +            gen_base_offset_addr(ctx, taddr, rs, s10 << df); + +            switch (MASK_MSA_MINOR(opcode)) { +            case OPC_LD_B: +                gen_helper_msa_ld_b(cpu_env, twd, taddr); +                break; +            case OPC_LD_H: +                gen_helper_msa_ld_h(cpu_env, twd, taddr); +                break; +            case OPC_LD_W: +                gen_helper_msa_ld_w(cpu_env, twd, taddr); +                break; +            case OPC_LD_D: +                gen_helper_msa_ld_d(cpu_env, twd, taddr); +                break; +            case OPC_ST_B: +                gen_helper_msa_st_b(cpu_env, twd, taddr); +                break; +            case OPC_ST_H: +                gen_helper_msa_st_h(cpu_env, twd, taddr); +                break; +            case OPC_ST_W: +                gen_helper_msa_st_w(cpu_env, twd, taddr); +                break; +            case OPC_ST_D: +                gen_helper_msa_st_d(cpu_env, twd, taddr); +                break; +            } + +            tcg_temp_free_i32(twd); +            tcg_temp_free(taddr); +        } +        break; +    default: +        MIPS_INVAL("MSA instruction"); +        generate_exception(ctx, EXCP_RI); +        break; +    } + +} + +static void decode_opc(CPUMIPSState *env, DisasContext *ctx) +{ +    int32_t offset; +    int rs, rt, rd, sa; +    uint32_t op, op1; +    int16_t imm; + +    /* make sure instructions are on a word boundary */ +    if (ctx->pc & 0x3) { +        env->CP0_BadVAddr = ctx->pc; +        generate_exception_err(ctx, EXCP_AdEL, EXCP_INST_NOTAVAIL); +        ctx->bstate = BS_STOP; +        return; +    } + +    /* Handle blikely not taken case */ +    if ((ctx->hflags & MIPS_HFLAG_BMASK_BASE) == MIPS_HFLAG_BL) { +        TCGLabel *l1 = gen_new_label(); + +        MIPS_DEBUG("blikely condition (" TARGET_FMT_lx ")", ctx->pc + 4); +        tcg_gen_brcondi_tl(TCG_COND_NE, bcond, 0, l1); +        tcg_gen_movi_i32(hflags, ctx->hflags & ~MIPS_HFLAG_BMASK); +        gen_goto_tb(ctx, 1, ctx->pc + 4); +        gen_set_label(l1); +    } + +    if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { +        tcg_gen_debug_insn_start(ctx->pc); +    } + +    op = MASK_OP_MAJOR(ctx->opcode); +    rs = (ctx->opcode >> 21) & 0x1f; +    rt = (ctx->opcode >> 16) & 0x1f; +    rd = (ctx->opcode >> 11) & 0x1f; +    sa = (ctx->opcode >> 6) & 0x1f; +    imm = (int16_t)ctx->opcode; +    switch (op) { +    case OPC_SPECIAL: +        decode_opc_special(env, ctx); +        break; +    case OPC_SPECIAL2: +        decode_opc_special2_legacy(env, ctx); +        break; +    case OPC_SPECIAL3: +        decode_opc_special3(env, ctx); +        break; +    case OPC_REGIMM: +        op1 = MASK_REGIMM(ctx->opcode); +        switch (op1) { +        case OPC_BLTZL: /* REGIMM branches */ +        case OPC_BGEZL: +        case OPC_BLTZALL: +        case OPC_BGEZALL: +            check_insn(ctx, ISA_MIPS2); +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            /* Fallthrough */ +        case OPC_BLTZ: +        case OPC_BGEZ: +            gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2, 4); +            break; +        case OPC_BLTZAL: +        case OPC_BGEZAL: +            if (ctx->insn_flags & ISA_MIPS32R6) { +                if (rs == 0) { +                    /* OPC_NAL, OPC_BAL */ +                    gen_compute_branch(ctx, op1, 4, 0, -1, imm << 2, 4); +                } else { +                    generate_exception(ctx, EXCP_RI); +                } +            } else { +                gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2, 4); +            } +            break; +        case OPC_TGEI ... OPC_TEQI: /* REGIMM traps */ +        case OPC_TNEI: +            check_insn(ctx, ISA_MIPS2); +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_trap(ctx, op1, rs, -1, imm); +            break; +        case OPC_SYNCI: +            check_insn(ctx, ISA_MIPS32R2); +            /* Break the TB to be able to sync copied instructions +               immediately */ +            ctx->bstate = BS_STOP; +            break; +        case OPC_BPOSGE32:    /* MIPS DSP branch */ +#if defined(TARGET_MIPS64) +        case OPC_BPOSGE64: +#endif +            check_dsp(ctx); +            gen_compute_branch(ctx, op1, 4, -1, -2, (int32_t)imm << 2, 4); +            break; +#if defined(TARGET_MIPS64) +        case OPC_DAHI: +            check_insn(ctx, ISA_MIPS32R6); +            check_mips_64(ctx); +            if (rs != 0) { +                tcg_gen_addi_tl(cpu_gpr[rs], cpu_gpr[rs], (int64_t)imm << 32); +            } +            MIPS_DEBUG("dahi %s, %04x", regnames[rs], imm); +            break; +        case OPC_DATI: +            check_insn(ctx, ISA_MIPS32R6); +            check_mips_64(ctx); +            if (rs != 0) { +                tcg_gen_addi_tl(cpu_gpr[rs], cpu_gpr[rs], (int64_t)imm << 48); +            } +            MIPS_DEBUG("dati %s, %04x", regnames[rs], imm); +            break; +#endif +        default:            /* Invalid */ +            MIPS_INVAL("regimm"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_CP0: +        check_cp0_enabled(ctx); +        op1 = MASK_CP0(ctx->opcode); +        switch (op1) { +        case OPC_MFC0: +        case OPC_MTC0: +        case OPC_MFTR: +        case OPC_MTTR: +        case OPC_MFHC0: +        case OPC_MTHC0: +#if defined(TARGET_MIPS64) +        case OPC_DMFC0: +        case OPC_DMTC0: +#endif +#ifndef CONFIG_USER_ONLY +            gen_cp0(env, ctx, op1, rt, rd); +#endif /* !CONFIG_USER_ONLY */ +            break; +        case OPC_C0_FIRST ... OPC_C0_LAST: +#ifndef CONFIG_USER_ONLY +            gen_cp0(env, ctx, MASK_C0(ctx->opcode), rt, rd); +#endif /* !CONFIG_USER_ONLY */ +            break; +        case OPC_MFMC0: +#ifndef CONFIG_USER_ONLY +            { +                uint32_t op2; +                TCGv t0 = tcg_temp_new(); + +                op2 = MASK_MFMC0(ctx->opcode); +                switch (op2) { +                case OPC_DMT: +                    check_insn(ctx, ASE_MT); +                    gen_helper_dmt(t0); +                    gen_store_gpr(t0, rt); +                    break; +                case OPC_EMT: +                    check_insn(ctx, ASE_MT); +                    gen_helper_emt(t0); +                    gen_store_gpr(t0, rt); +                    break; +                case OPC_DVPE: +                    check_insn(ctx, ASE_MT); +                    gen_helper_dvpe(t0, cpu_env); +                    gen_store_gpr(t0, rt); +                    break; +                case OPC_EVPE: +                    check_insn(ctx, ASE_MT); +                    gen_helper_evpe(t0, cpu_env); +                    gen_store_gpr(t0, rt); +                    break; +                case OPC_DI: +                    check_insn(ctx, ISA_MIPS32R2); +                    save_cpu_state(ctx, 1); +                    gen_helper_di(t0, cpu_env); +                    gen_store_gpr(t0, rt); +                    /* Stop translation as we may have switched +                       the execution mode.  */ +                    ctx->bstate = BS_STOP; +                    break; +                case OPC_EI: +                    check_insn(ctx, ISA_MIPS32R2); +                    save_cpu_state(ctx, 1); +                    gen_helper_ei(t0, cpu_env); +                    gen_store_gpr(t0, rt); +                    /* Stop translation as we may have switched +                       the execution mode.  */ +                    ctx->bstate = BS_STOP; +                    break; +                default:            /* Invalid */ +                    MIPS_INVAL("mfmc0"); +                    generate_exception(ctx, EXCP_RI); +                    break; +                } +                tcg_temp_free(t0); +            } +#endif /* !CONFIG_USER_ONLY */ +            break; +        case OPC_RDPGPR: +            check_insn(ctx, ISA_MIPS32R2); +            gen_load_srsgpr(rt, rd); +            break; +        case OPC_WRPGPR: +            check_insn(ctx, ISA_MIPS32R2); +            gen_store_srsgpr(rt, rd); +            break; +        default: +            MIPS_INVAL("cp0"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; +    case OPC_BOVC: /* OPC_BEQZALC, OPC_BEQC, OPC_ADDI */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_BOVC, OPC_BEQZALC, OPC_BEQC */ +            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); +        } else { +            /* OPC_ADDI */ +            /* Arithmetic with immediate opcode */ +            gen_arith_imm(ctx, op, rt, rs, imm); +        } +        break; +    case OPC_ADDIU: +         gen_arith_imm(ctx, op, rt, rs, imm); +         break; +    case OPC_SLTI: /* Set on less than with immediate opcode */ +    case OPC_SLTIU: +         gen_slt_imm(ctx, op, rt, rs, imm); +         break; +    case OPC_ANDI: /* Arithmetic with immediate opcode */ +    case OPC_LUI: /* OPC_AUI */ +    case OPC_ORI: +    case OPC_XORI: +         gen_logic_imm(ctx, op, rt, rs, imm); +         break; +    case OPC_J ... OPC_JAL: /* Jump */ +         offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; +         gen_compute_branch(ctx, op, 4, rs, rt, offset, 4); +         break; +    /* Branch */ +    case OPC_BLEZC: /* OPC_BGEZC, OPC_BGEC, OPC_BLEZL */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            if (rt == 0) { +                generate_exception(ctx, EXCP_RI); +                break; +            } +            /* OPC_BLEZC, OPC_BGEZC, OPC_BGEC */ +            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); +        } else { +            /* OPC_BLEZL */ +            gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); +        } +        break; +    case OPC_BGTZC: /* OPC_BLTZC, OPC_BLTC, OPC_BGTZL */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            if (rt == 0) { +                generate_exception(ctx, EXCP_RI); +                break; +            } +            /* OPC_BGTZC, OPC_BLTZC, OPC_BLTC */ +            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); +        } else { +            /* OPC_BGTZL */ +            gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); +        } +        break; +    case OPC_BLEZALC: /* OPC_BGEZALC, OPC_BGEUC, OPC_BLEZ */ +        if (rt == 0) { +            /* OPC_BLEZ */ +            gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); +        } else { +            check_insn(ctx, ISA_MIPS32R6); +            /* OPC_BLEZALC, OPC_BGEZALC, OPC_BGEUC */ +            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); +        } +        break; +    case OPC_BGTZALC: /* OPC_BLTZALC, OPC_BLTUC, OPC_BGTZ */ +        if (rt == 0) { +            /* OPC_BGTZ */ +            gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); +        } else { +            check_insn(ctx, ISA_MIPS32R6); +            /* OPC_BGTZALC, OPC_BLTZALC, OPC_BLTUC */ +            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); +        } +        break; +    case OPC_BEQL: +    case OPC_BNEL: +        check_insn(ctx, ISA_MIPS2); +         check_insn_opc_removed(ctx, ISA_MIPS32R6); +        /* Fallthrough */ +    case OPC_BEQ: +    case OPC_BNE: +         gen_compute_branch(ctx, op, 4, rs, rt, imm << 2, 4); +         break; +    case OPC_LL: /* Load and stores */ +        check_insn(ctx, ISA_MIPS2); +        /* Fallthrough */ +    case OPC_LWL: +    case OPC_LWR: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +         /* Fallthrough */ +    case OPC_LB ... OPC_LH: +    case OPC_LW ... OPC_LHU: +         gen_ld(ctx, op, rt, rs, imm); +         break; +    case OPC_SWL: +    case OPC_SWR: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        /* fall through */ +    case OPC_SB ... OPC_SH: +    case OPC_SW: +         gen_st(ctx, op, rt, rs, imm); +         break; +    case OPC_SC: +        check_insn(ctx, ISA_MIPS2); +         check_insn_opc_removed(ctx, ISA_MIPS32R6); +         gen_st_cond(ctx, op, rt, rs, imm); +         break; +    case OPC_CACHE: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        check_cp0_enabled(ctx); +        check_insn(ctx, ISA_MIPS3 | ISA_MIPS32); +        /* Treat as NOP. */ +        break; +    case OPC_PREF: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); +        /* Treat as NOP. */ +        break; + +    /* Floating point (COP1). */ +    case OPC_LWC1: +    case OPC_LDC1: +    case OPC_SWC1: +    case OPC_SDC1: +        gen_cop1_ldst(ctx, op, rt, rs, imm); +        break; + +    case OPC_CP1: +        op1 = MASK_CP1(ctx->opcode); + +        switch (op1) { +        case OPC_MFHC1: +        case OPC_MTHC1: +            check_cp1_enabled(ctx); +            check_insn(ctx, ISA_MIPS32R2); +        case OPC_MFC1: +        case OPC_CFC1: +        case OPC_MTC1: +        case OPC_CTC1: +            check_cp1_enabled(ctx); +            gen_cp1(ctx, op1, rt, rd); +            break; +#if defined(TARGET_MIPS64) +        case OPC_DMFC1: +        case OPC_DMTC1: +            check_cp1_enabled(ctx); +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_cp1(ctx, op1, rt, rd); +            break; +#endif +        case OPC_BC1EQZ: /* OPC_BC1ANY2 */ +            check_cp1_enabled(ctx); +            if (ctx->insn_flags & ISA_MIPS32R6) { +                /* OPC_BC1EQZ */ +                gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), +                                       rt, imm << 2, 4); +            } else { +                /* OPC_BC1ANY2 */ +                check_cop1x(ctx); +                check_insn(ctx, ASE_MIPS3D); +                gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), +                                    (rt >> 2) & 0x7, imm << 2); +            } +            break; +        case OPC_BC1NEZ: +            check_cp1_enabled(ctx); +            check_insn(ctx, ISA_MIPS32R6); +            gen_compute_branch1_r6(ctx, MASK_CP1(ctx->opcode), +                                   rt, imm << 2, 4); +            break; +        case OPC_BC1ANY4: +            check_cp1_enabled(ctx); +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            check_cop1x(ctx); +            check_insn(ctx, ASE_MIPS3D); +            /* fall through */ +        case OPC_BC1: +            check_cp1_enabled(ctx); +            check_insn_opc_removed(ctx, ISA_MIPS32R6); +            gen_compute_branch1(ctx, MASK_BC1(ctx->opcode), +                                (rt >> 2) & 0x7, imm << 2); +            break; +        case OPC_PS_FMT: +            check_ps(ctx); +            /* fall through */ +        case OPC_S_FMT: +        case OPC_D_FMT: +            check_cp1_enabled(ctx); +            gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, +                       (imm >> 8) & 0x7); +            break; +        case OPC_W_FMT: +        case OPC_L_FMT: +        { +            int r6_op = ctx->opcode & FOP(0x3f, 0x1f); +            check_cp1_enabled(ctx); +            if (ctx->insn_flags & ISA_MIPS32R6) { +                switch (r6_op) { +                case R6_OPC_CMP_AF_S: +                case R6_OPC_CMP_UN_S: +                case R6_OPC_CMP_EQ_S: +                case R6_OPC_CMP_UEQ_S: +                case R6_OPC_CMP_LT_S: +                case R6_OPC_CMP_ULT_S: +                case R6_OPC_CMP_LE_S: +                case R6_OPC_CMP_ULE_S: +                case R6_OPC_CMP_SAF_S: +                case R6_OPC_CMP_SUN_S: +                case R6_OPC_CMP_SEQ_S: +                case R6_OPC_CMP_SEUQ_S: +                case R6_OPC_CMP_SLT_S: +                case R6_OPC_CMP_SULT_S: +                case R6_OPC_CMP_SLE_S: +                case R6_OPC_CMP_SULE_S: +                case R6_OPC_CMP_OR_S: +                case R6_OPC_CMP_UNE_S: +                case R6_OPC_CMP_NE_S: +                case R6_OPC_CMP_SOR_S: +                case R6_OPC_CMP_SUNE_S: +                case R6_OPC_CMP_SNE_S: +                    gen_r6_cmp_s(ctx, ctx->opcode & 0x1f, rt, rd, sa); +                    break; +                case R6_OPC_CMP_AF_D: +                case R6_OPC_CMP_UN_D: +                case R6_OPC_CMP_EQ_D: +                case R6_OPC_CMP_UEQ_D: +                case R6_OPC_CMP_LT_D: +                case R6_OPC_CMP_ULT_D: +                case R6_OPC_CMP_LE_D: +                case R6_OPC_CMP_ULE_D: +                case R6_OPC_CMP_SAF_D: +                case R6_OPC_CMP_SUN_D: +                case R6_OPC_CMP_SEQ_D: +                case R6_OPC_CMP_SEUQ_D: +                case R6_OPC_CMP_SLT_D: +                case R6_OPC_CMP_SULT_D: +                case R6_OPC_CMP_SLE_D: +                case R6_OPC_CMP_SULE_D: +                case R6_OPC_CMP_OR_D: +                case R6_OPC_CMP_UNE_D: +                case R6_OPC_CMP_NE_D: +                case R6_OPC_CMP_SOR_D: +                case R6_OPC_CMP_SUNE_D: +                case R6_OPC_CMP_SNE_D: +                    gen_r6_cmp_d(ctx, ctx->opcode & 0x1f, rt, rd, sa); +                    break; +                default: +                    gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), +                               rt, rd, sa, (imm >> 8) & 0x7); + +                    break; +                } +            } else { +                gen_farith(ctx, ctx->opcode & FOP(0x3f, 0x1f), rt, rd, sa, +                           (imm >> 8) & 0x7); +            } +            break; +        } +        case OPC_BZ_V: +        case OPC_BNZ_V: +        case OPC_BZ_B: +        case OPC_BZ_H: +        case OPC_BZ_W: +        case OPC_BZ_D: +        case OPC_BNZ_B: +        case OPC_BNZ_H: +        case OPC_BNZ_W: +        case OPC_BNZ_D: +            check_insn(ctx, ASE_MSA); +            gen_msa_branch(env, ctx, op1); +            break; +        default: +            MIPS_INVAL("cp1"); +            generate_exception(ctx, EXCP_RI); +            break; +        } +        break; + +    /* Compact branches [R6] and COP2 [non-R6] */ +    case OPC_BC: /* OPC_LWC2 */ +    case OPC_BALC: /* OPC_SWC2 */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_BC, OPC_BALC */ +            gen_compute_compact_branch(ctx, op, 0, 0, +                                       sextract32(ctx->opcode << 2, 0, 28)); +        } else { +            /* OPC_LWC2, OPC_SWC2 */ +            /* COP2: Not implemented. */ +            generate_exception_err(ctx, EXCP_CpU, 2); +        } +        break; +    case OPC_BEQZC: /* OPC_JIC, OPC_LDC2 */ +    case OPC_BNEZC: /* OPC_JIALC, OPC_SDC2 */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            if (rs != 0) { +                /* OPC_BEQZC, OPC_BNEZC */ +                gen_compute_compact_branch(ctx, op, rs, 0, +                                           sextract32(ctx->opcode << 2, 0, 23)); +            } else { +                /* OPC_JIC, OPC_JIALC */ +                gen_compute_compact_branch(ctx, op, 0, rt, imm); +            } +        } else { +            /* OPC_LWC2, OPC_SWC2 */ +            /* COP2: Not implemented. */ +            generate_exception_err(ctx, EXCP_CpU, 2); +        } +        break; +    case OPC_CP2: +        check_insn(ctx, INSN_LOONGSON2F); +        /* Note that these instructions use different fields.  */ +        gen_loongson_multimedia(ctx, sa, rd, rt); +        break; + +    case OPC_CP3: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { +            check_cp1_enabled(ctx); +            op1 = MASK_CP3(ctx->opcode); +            switch (op1) { +            case OPC_LUXC1: +            case OPC_SUXC1: +                check_insn(ctx, ISA_MIPS5 | ISA_MIPS32R2); +                /* Fallthrough */ +            case OPC_LWXC1: +            case OPC_LDXC1: +            case OPC_SWXC1: +            case OPC_SDXC1: +                check_insn(ctx, ISA_MIPS4 | ISA_MIPS32R2); +                gen_flt3_ldst(ctx, op1, sa, rd, rs, rt); +                break; +            case OPC_PREFX: +                check_insn(ctx, ISA_MIPS4 | ISA_MIPS32R2); +                /* Treat as NOP. */ +                break; +            case OPC_ALNV_PS: +                check_insn(ctx, ISA_MIPS5 | ISA_MIPS32R2); +                /* Fallthrough */ +            case OPC_MADD_S: +            case OPC_MADD_D: +            case OPC_MADD_PS: +            case OPC_MSUB_S: +            case OPC_MSUB_D: +            case OPC_MSUB_PS: +            case OPC_NMADD_S: +            case OPC_NMADD_D: +            case OPC_NMADD_PS: +            case OPC_NMSUB_S: +            case OPC_NMSUB_D: +            case OPC_NMSUB_PS: +                check_insn(ctx, ISA_MIPS4 | ISA_MIPS32R2); +                gen_flt3_arith(ctx, op1, sa, rs, rd, rt); +                break; +            default: +                MIPS_INVAL("cp3"); +                generate_exception (ctx, EXCP_RI); +                break; +            } +        } else { +            generate_exception_err(ctx, EXCP_CpU, 1); +        } +        break; + +#if defined(TARGET_MIPS64) +    /* MIPS64 opcodes */ +    case OPC_LDL ... OPC_LDR: +    case OPC_LLD: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        /* fall through */ +    case OPC_LWU: +    case OPC_LD: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_ld(ctx, op, rt, rs, imm); +        break; +    case OPC_SDL ... OPC_SDR: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        /* fall through */ +    case OPC_SD: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_st(ctx, op, rt, rs, imm); +        break; +    case OPC_SCD: +        check_insn_opc_removed(ctx, ISA_MIPS32R6); +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_st_cond(ctx, op, rt, rs, imm); +        break; +    case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC, OPC_DADDI */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            /* OPC_BNVC, OPC_BNEZALC, OPC_BNEC */ +            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); +        } else { +            /* OPC_DADDI */ +            check_insn(ctx, ISA_MIPS3); +            check_mips_64(ctx); +            gen_arith_imm(ctx, op, rt, rs, imm); +        } +        break; +    case OPC_DADDIU: +        check_insn(ctx, ISA_MIPS3); +        check_mips_64(ctx); +        gen_arith_imm(ctx, op, rt, rs, imm); +        break; +#else +    case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +            gen_compute_compact_branch(ctx, op, rs, rt, imm << 2); +        } else { +            MIPS_INVAL("major opcode"); +            generate_exception(ctx, EXCP_RI); +        } +        break; +#endif +    case OPC_DAUI: /* OPC_JALX */ +        if (ctx->insn_flags & ISA_MIPS32R6) { +#if defined(TARGET_MIPS64) +            /* OPC_DAUI */ +            check_mips_64(ctx); +            if (rt != 0) { +                TCGv t0 = tcg_temp_new(); +                gen_load_gpr(t0, rs); +                tcg_gen_addi_tl(cpu_gpr[rt], t0, imm << 16); +                tcg_temp_free(t0); +            } +            MIPS_DEBUG("daui %s, %s, %04x", regnames[rt], regnames[rs], imm); +#else +            generate_exception(ctx, EXCP_RI); +            MIPS_INVAL("major opcode"); +#endif +        } else { +            /* OPC_JALX */ +            check_insn(ctx, ASE_MIPS16 | ASE_MICROMIPS); +            offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; +            gen_compute_branch(ctx, op, 4, rs, rt, offset, 4); +        } +        break; +    case OPC_MSA: /* OPC_MDMX */ +        /* MDMX: Not implemented. */ +        gen_msa(env, ctx); +        break; +    case OPC_PCREL: +        check_insn(ctx, ISA_MIPS32R6); +        gen_pcrel(ctx, ctx->opcode, ctx->pc, rs); +        break; +    default:            /* Invalid */ +        MIPS_INVAL("major opcode"); +        generate_exception(ctx, EXCP_RI); +        break; +    } +} + +static inline void +gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb, +                               bool search_pc) +{ +    CPUState *cs = CPU(cpu); +    CPUMIPSState *env = &cpu->env; +    DisasContext ctx; +    target_ulong pc_start; +    target_ulong next_page_start; +    CPUBreakpoint *bp; +    int j, lj = -1; +    int num_insns; +    int max_insns; +    int insn_bytes; +    int is_slot; + +    if (search_pc) +        qemu_log("search pc %d\n", search_pc); + +    pc_start = tb->pc; +    next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; +    ctx.pc = pc_start; +    ctx.saved_pc = -1; +    ctx.singlestep_enabled = cs->singlestep_enabled; +    ctx.insn_flags = env->insn_flags; +    ctx.CP0_Config1 = env->CP0_Config1; +    ctx.tb = tb; +    ctx.bstate = BS_NONE; +    ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; +    ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; +    ctx.ie = (env->CP0_Config4 >> CP0C4_IE) & 3; +    ctx.bi = (env->CP0_Config3 >> CP0C3_BI) & 1; +    ctx.bp = (env->CP0_Config3 >> CP0C3_BP) & 1; +    ctx.PAMask = env->PAMask; +    ctx.mvh = (env->CP0_Config5 >> CP0C5_MVH) & 1; +    ctx.CP0_LLAddr_shift = env->CP0_LLAddr_shift; +    /* Restore delay slot state from the tb context.  */ +    ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */ +    ctx.ulri = (env->CP0_Config3 >> CP0C3_ULRI) & 1; +    ctx.ps = ((env->active_fpu.fcr0 >> FCR0_PS) & 1) || +             (env->insn_flags & (INSN_LOONGSON2E | INSN_LOONGSON2F)); +    restore_cpu_state(env, &ctx); +#ifdef CONFIG_USER_ONLY +        ctx.mem_idx = MIPS_HFLAG_UM; +#else +        ctx.mem_idx = ctx.hflags & MIPS_HFLAG_KSU; +#endif +    ctx.default_tcg_memop_mask = (ctx.insn_flags & ISA_MIPS32R6) ? +                                 MO_UNALN : MO_ALIGN; +    num_insns = 0; +    max_insns = tb->cflags & CF_COUNT_MASK; +    if (max_insns == 0) +        max_insns = CF_COUNT_MASK; +    LOG_DISAS("\ntb %p idx %d hflags %04x\n", tb, ctx.mem_idx, ctx.hflags); +    gen_tb_start(tb); +    while (ctx.bstate == BS_NONE) { +        if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { +            QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { +                if (bp->pc == ctx.pc) { +                    save_cpu_state(&ctx, 1); +                    ctx.bstate = BS_BRANCH; +                    gen_helper_0e0i(raise_exception, EXCP_DEBUG); +                    /* Include the breakpoint location or the tb won't +                     * be flushed when it must be.  */ +                    ctx.pc += 4; +                    goto done_generating; +                } +            } +        } + +        if (search_pc) { +            j = tcg_op_buf_count(); +            if (lj < j) { +                lj++; +                while (lj < j) +                    tcg_ctx.gen_opc_instr_start[lj++] = 0; +            } +            tcg_ctx.gen_opc_pc[lj] = ctx.pc; +            gen_opc_hflags[lj] = ctx.hflags & MIPS_HFLAG_BMASK; +            gen_opc_btarget[lj] = ctx.btarget; +            tcg_ctx.gen_opc_instr_start[lj] = 1; +            tcg_ctx.gen_opc_icount[lj] = num_insns; +        } +        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) +            gen_io_start(); + +        is_slot = ctx.hflags & MIPS_HFLAG_BMASK; +        if (!(ctx.hflags & MIPS_HFLAG_M16)) { +            ctx.opcode = cpu_ldl_code(env, ctx.pc); +            insn_bytes = 4; +            decode_opc(env, &ctx); +        } else if (ctx.insn_flags & ASE_MICROMIPS) { +            ctx.opcode = cpu_lduw_code(env, ctx.pc); +            insn_bytes = decode_micromips_opc(env, &ctx); +        } else if (ctx.insn_flags & ASE_MIPS16) { +            ctx.opcode = cpu_lduw_code(env, ctx.pc); +            insn_bytes = decode_mips16_opc(env, &ctx); +        } else { +            generate_exception(&ctx, EXCP_RI); +            ctx.bstate = BS_STOP; +            break; +        } + +        if (ctx.hflags & MIPS_HFLAG_BMASK) { +            if (!(ctx.hflags & (MIPS_HFLAG_BDS16 | MIPS_HFLAG_BDS32 | +                                MIPS_HFLAG_FBNSLOT))) { +                /* force to generate branch as there is neither delay nor +                   forbidden slot */ +                is_slot = 1; +            } +            if ((ctx.hflags & MIPS_HFLAG_M16) && +                (ctx.hflags & MIPS_HFLAG_FBNSLOT)) { +                /* Force to generate branch as microMIPS R6 doesn't restrict +                   branches in the forbidden slot. */ +                is_slot = 1; +            } +        } +        if (is_slot) { +            gen_branch(&ctx, insn_bytes); +        } +        ctx.pc += insn_bytes; + +        num_insns++; + +        /* Execute a branch and its delay slot as a single instruction. +           This is what GDB expects and is consistent with what the +           hardware does (e.g. if a delay slot instruction faults, the +           reported PC is the PC of the branch).  */ +        if (cs->singlestep_enabled && (ctx.hflags & MIPS_HFLAG_BMASK) == 0) { +            break; +        } + +        if (ctx.pc >= next_page_start) { +            break; +        } + +        if (tcg_op_buf_full()) { +            break; +        } + +        if (num_insns >= max_insns) +            break; + +        if (singlestep) +            break; +    } +    if (tb->cflags & CF_LAST_IO) { +        gen_io_end(); +    } +    if (cs->singlestep_enabled && ctx.bstate != BS_BRANCH) { +        save_cpu_state(&ctx, ctx.bstate != BS_EXCP); +        gen_helper_0e0i(raise_exception, EXCP_DEBUG); +    } else { +        switch (ctx.bstate) { +        case BS_STOP: +            gen_goto_tb(&ctx, 0, ctx.pc); +            break; +        case BS_NONE: +            save_cpu_state(&ctx, 0); +            gen_goto_tb(&ctx, 0, ctx.pc); +            break; +        case BS_EXCP: +            tcg_gen_exit_tb(0); +            break; +        case BS_BRANCH: +        default: +            break; +        } +    } +done_generating: +    gen_tb_end(tb, num_insns); + +    if (search_pc) { +        j = tcg_op_buf_count(); +        lj++; +        while (lj <= j) +            tcg_ctx.gen_opc_instr_start[lj++] = 0; +    } else { +        tb->size = ctx.pc - pc_start; +        tb->icount = num_insns; +    } +#ifdef DEBUG_DISAS +    LOG_DISAS("\n"); +    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { +        qemu_log("IN: %s\n", lookup_symbol(pc_start)); +        log_target_disas(cs, pc_start, ctx.pc - pc_start, 0); +        qemu_log("\n"); +    } +#endif +} + +void gen_intermediate_code (CPUMIPSState *env, struct TranslationBlock *tb) +{ +    gen_intermediate_code_internal(mips_env_get_cpu(env), tb, false); +} + +void gen_intermediate_code_pc (CPUMIPSState *env, struct TranslationBlock *tb) +{ +    gen_intermediate_code_internal(mips_env_get_cpu(env), tb, true); +} + +static void fpu_dump_state(CPUMIPSState *env, FILE *f, fprintf_function fpu_fprintf, +                           int flags) +{ +    int i; +    int is_fpu64 = !!(env->hflags & MIPS_HFLAG_F64); + +#define printfpr(fp)                                                    \ +    do {                                                                \ +        if (is_fpu64)                                                   \ +            fpu_fprintf(f, "w:%08x d:%016" PRIx64                       \ +                        " fd:%13g fs:%13g psu: %13g\n",                 \ +                        (fp)->w[FP_ENDIAN_IDX], (fp)->d,                \ +                        (double)(fp)->fd,                               \ +                        (double)(fp)->fs[FP_ENDIAN_IDX],                \ +                        (double)(fp)->fs[!FP_ENDIAN_IDX]);              \ +        else {                                                          \ +            fpr_t tmp;                                                  \ +            tmp.w[FP_ENDIAN_IDX] = (fp)->w[FP_ENDIAN_IDX];              \ +            tmp.w[!FP_ENDIAN_IDX] = ((fp) + 1)->w[FP_ENDIAN_IDX];       \ +            fpu_fprintf(f, "w:%08x d:%016" PRIx64                       \ +                        " fd:%13g fs:%13g psu:%13g\n",                  \ +                        tmp.w[FP_ENDIAN_IDX], tmp.d,                    \ +                        (double)tmp.fd,                                 \ +                        (double)tmp.fs[FP_ENDIAN_IDX],                  \ +                        (double)tmp.fs[!FP_ENDIAN_IDX]);                \ +        }                                                               \ +    } while(0) + + +    fpu_fprintf(f, "CP1 FCR0 0x%08x  FCR31 0x%08x  SR.FR %d  fp_status 0x%02x\n", +                env->active_fpu.fcr0, env->active_fpu.fcr31, is_fpu64, +                get_float_exception_flags(&env->active_fpu.fp_status)); +    for (i = 0; i < 32; (is_fpu64) ? i++ : (i += 2)) { +        fpu_fprintf(f, "%3s: ", fregnames[i]); +        printfpr(&env->active_fpu.fpr[i]); +    } + +#undef printfpr +} + +#if defined(TARGET_MIPS64) && defined(MIPS_DEBUG_SIGN_EXTENSIONS) +/* Debug help: The architecture requires 32bit code to maintain proper +   sign-extended values on 64bit machines.  */ + +#define SIGN_EXT_P(val) ((((val) & ~0x7fffffff) == 0) || (((val) & ~0x7fffffff) == ~0x7fffffff)) + +static void +cpu_mips_check_sign_extensions (CPUMIPSState *env, FILE *f, +                                fprintf_function cpu_fprintf, +                                int flags) +{ +    int i; + +    if (!SIGN_EXT_P(env->active_tc.PC)) +        cpu_fprintf(f, "BROKEN: pc=0x" TARGET_FMT_lx "\n", env->active_tc.PC); +    if (!SIGN_EXT_P(env->active_tc.HI[0])) +        cpu_fprintf(f, "BROKEN: HI=0x" TARGET_FMT_lx "\n", env->active_tc.HI[0]); +    if (!SIGN_EXT_P(env->active_tc.LO[0])) +        cpu_fprintf(f, "BROKEN: LO=0x" TARGET_FMT_lx "\n", env->active_tc.LO[0]); +    if (!SIGN_EXT_P(env->btarget)) +        cpu_fprintf(f, "BROKEN: btarget=0x" TARGET_FMT_lx "\n", env->btarget); + +    for (i = 0; i < 32; i++) { +        if (!SIGN_EXT_P(env->active_tc.gpr[i])) +            cpu_fprintf(f, "BROKEN: %s=0x" TARGET_FMT_lx "\n", regnames[i], env->active_tc.gpr[i]); +    } + +    if (!SIGN_EXT_P(env->CP0_EPC)) +        cpu_fprintf(f, "BROKEN: EPC=0x" TARGET_FMT_lx "\n", env->CP0_EPC); +    if (!SIGN_EXT_P(env->lladdr)) +        cpu_fprintf(f, "BROKEN: LLAddr=0x" TARGET_FMT_lx "\n", env->lladdr); +} +#endif + +void mips_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, +                         int flags) +{ +    MIPSCPU *cpu = MIPS_CPU(cs); +    CPUMIPSState *env = &cpu->env; +    int i; + +    cpu_fprintf(f, "pc=0x" TARGET_FMT_lx " HI=0x" TARGET_FMT_lx +                " LO=0x" TARGET_FMT_lx " ds %04x " +                TARGET_FMT_lx " " TARGET_FMT_ld "\n", +                env->active_tc.PC, env->active_tc.HI[0], env->active_tc.LO[0], +                env->hflags, env->btarget, env->bcond); +    for (i = 0; i < 32; i++) { +        if ((i & 3) == 0) +            cpu_fprintf(f, "GPR%02d:", i); +        cpu_fprintf(f, " %s " TARGET_FMT_lx, regnames[i], env->active_tc.gpr[i]); +        if ((i & 3) == 3) +            cpu_fprintf(f, "\n"); +    } + +    cpu_fprintf(f, "CP0 Status  0x%08x Cause   0x%08x EPC    0x" TARGET_FMT_lx "\n", +                env->CP0_Status, env->CP0_Cause, env->CP0_EPC); +    cpu_fprintf(f, "    Config0 0x%08x Config1 0x%08x LLAddr 0x%016" +                PRIx64 "\n", +                env->CP0_Config0, env->CP0_Config1, env->lladdr); +    cpu_fprintf(f, "    Config2 0x%08x Config3 0x%08x\n", +                env->CP0_Config2, env->CP0_Config3); +    cpu_fprintf(f, "    Config4 0x%08x Config5 0x%08x\n", +                env->CP0_Config4, env->CP0_Config5); +    if (env->hflags & MIPS_HFLAG_FPU) +        fpu_dump_state(env, f, cpu_fprintf, flags); +#if defined(TARGET_MIPS64) && defined(MIPS_DEBUG_SIGN_EXTENSIONS) +    cpu_mips_check_sign_extensions(env, f, cpu_fprintf, flags); +#endif +} + +void mips_tcg_init(void) +{ +    int i; +    static int inited; + +    /* Initialize various static tables. */ +    if (inited) +        return; + +    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); +    TCGV_UNUSED(cpu_gpr[0]); +    for (i = 1; i < 32; i++) +        cpu_gpr[i] = tcg_global_mem_new(TCG_AREG0, +                                        offsetof(CPUMIPSState, active_tc.gpr[i]), +                                        regnames[i]); + +    for (i = 0; i < 32; i++) { +        int off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[0]); +        msa_wr_d[i * 2] = +                tcg_global_mem_new_i64(TCG_AREG0, off, msaregnames[i * 2]); +        /* The scalar floating-point unit (FPU) registers are mapped on +         * the MSA vector registers. */ +        fpu_f64[i] = msa_wr_d[i * 2]; +        off = offsetof(CPUMIPSState, active_fpu.fpr[i].wr.d[1]); +        msa_wr_d[i * 2 + 1] = +                tcg_global_mem_new_i64(TCG_AREG0, off, msaregnames[i * 2 + 1]); +    } + +    cpu_PC = tcg_global_mem_new(TCG_AREG0, +                                offsetof(CPUMIPSState, active_tc.PC), "PC"); +    for (i = 0; i < MIPS_DSP_ACC; i++) { +        cpu_HI[i] = tcg_global_mem_new(TCG_AREG0, +                                       offsetof(CPUMIPSState, active_tc.HI[i]), +                                       regnames_HI[i]); +        cpu_LO[i] = tcg_global_mem_new(TCG_AREG0, +                                       offsetof(CPUMIPSState, active_tc.LO[i]), +                                       regnames_LO[i]); +    } +    cpu_dspctrl = tcg_global_mem_new(TCG_AREG0, +                                     offsetof(CPUMIPSState, active_tc.DSPControl), +                                     "DSPControl"); +    bcond = tcg_global_mem_new(TCG_AREG0, +                               offsetof(CPUMIPSState, bcond), "bcond"); +    btarget = tcg_global_mem_new(TCG_AREG0, +                                 offsetof(CPUMIPSState, btarget), "btarget"); +    hflags = tcg_global_mem_new_i32(TCG_AREG0, +                                    offsetof(CPUMIPSState, hflags), "hflags"); + +    fpu_fcr0 = tcg_global_mem_new_i32(TCG_AREG0, +                                      offsetof(CPUMIPSState, active_fpu.fcr0), +                                      "fcr0"); +    fpu_fcr31 = tcg_global_mem_new_i32(TCG_AREG0, +                                       offsetof(CPUMIPSState, active_fpu.fcr31), +                                       "fcr31"); + +    inited = 1; +} + +#include "translate_init.c" + +MIPSCPU *cpu_mips_init(const char *cpu_model) +{ +    MIPSCPU *cpu; +    CPUMIPSState *env; +    const mips_def_t *def; + +    def = cpu_mips_find_by_name(cpu_model); +    if (!def) +        return NULL; +    cpu = MIPS_CPU(object_new(TYPE_MIPS_CPU)); +    env = &cpu->env; +    env->cpu_model = def; + +#ifndef CONFIG_USER_ONLY +    mmu_init(env, def); +#endif +    fpu_init(env, def); +    mvp_init(env, def); + +    object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + +    return cpu; +} + +void cpu_state_reset(CPUMIPSState *env) +{ +    MIPSCPU *cpu = mips_env_get_cpu(env); +    CPUState *cs = CPU(cpu); + +    /* Reset registers to their default values */ +    env->CP0_PRid = env->cpu_model->CP0_PRid; +    env->CP0_Config0 = env->cpu_model->CP0_Config0; +#ifdef TARGET_WORDS_BIGENDIAN +    env->CP0_Config0 |= (1 << CP0C0_BE); +#endif +    env->CP0_Config1 = env->cpu_model->CP0_Config1; +    env->CP0_Config2 = env->cpu_model->CP0_Config2; +    env->CP0_Config3 = env->cpu_model->CP0_Config3; +    env->CP0_Config4 = env->cpu_model->CP0_Config4; +    env->CP0_Config4_rw_bitmask = env->cpu_model->CP0_Config4_rw_bitmask; +    env->CP0_Config5 = env->cpu_model->CP0_Config5; +    env->CP0_Config5_rw_bitmask = env->cpu_model->CP0_Config5_rw_bitmask; +    env->CP0_Config6 = env->cpu_model->CP0_Config6; +    env->CP0_Config7 = env->cpu_model->CP0_Config7; +    env->CP0_LLAddr_rw_bitmask = env->cpu_model->CP0_LLAddr_rw_bitmask +                                 << env->cpu_model->CP0_LLAddr_shift; +    env->CP0_LLAddr_shift = env->cpu_model->CP0_LLAddr_shift; +    env->SYNCI_Step = env->cpu_model->SYNCI_Step; +    env->CCRes = env->cpu_model->CCRes; +    env->CP0_Status_rw_bitmask = env->cpu_model->CP0_Status_rw_bitmask; +    env->CP0_TCStatus_rw_bitmask = env->cpu_model->CP0_TCStatus_rw_bitmask; +    env->CP0_SRSCtl = env->cpu_model->CP0_SRSCtl; +    env->current_tc = 0; +    env->SEGBITS = env->cpu_model->SEGBITS; +    env->SEGMask = (target_ulong)((1ULL << env->cpu_model->SEGBITS) - 1); +#if defined(TARGET_MIPS64) +    if (env->cpu_model->insn_flags & ISA_MIPS3) { +        env->SEGMask |= 3ULL << 62; +    } +#endif +    env->PABITS = env->cpu_model->PABITS; +    env->CP0_SRSConf0_rw_bitmask = env->cpu_model->CP0_SRSConf0_rw_bitmask; +    env->CP0_SRSConf0 = env->cpu_model->CP0_SRSConf0; +    env->CP0_SRSConf1_rw_bitmask = env->cpu_model->CP0_SRSConf1_rw_bitmask; +    env->CP0_SRSConf1 = env->cpu_model->CP0_SRSConf1; +    env->CP0_SRSConf2_rw_bitmask = env->cpu_model->CP0_SRSConf2_rw_bitmask; +    env->CP0_SRSConf2 = env->cpu_model->CP0_SRSConf2; +    env->CP0_SRSConf3_rw_bitmask = env->cpu_model->CP0_SRSConf3_rw_bitmask; +    env->CP0_SRSConf3 = env->cpu_model->CP0_SRSConf3; +    env->CP0_SRSConf4_rw_bitmask = env->cpu_model->CP0_SRSConf4_rw_bitmask; +    env->CP0_SRSConf4 = env->cpu_model->CP0_SRSConf4; +    env->CP0_PageGrain_rw_bitmask = env->cpu_model->CP0_PageGrain_rw_bitmask; +    env->CP0_PageGrain = env->cpu_model->CP0_PageGrain; +    env->active_fpu.fcr0 = env->cpu_model->CP1_fcr0; +    env->msair = env->cpu_model->MSAIR; +    env->insn_flags = env->cpu_model->insn_flags; + +#if defined(CONFIG_USER_ONLY) +    env->CP0_Status = (MIPS_HFLAG_UM << CP0St_KSU); +# ifdef TARGET_MIPS64 +    /* Enable 64-bit register mode.  */ +    env->CP0_Status |= (1 << CP0St_PX); +# endif +# ifdef TARGET_ABI_MIPSN64 +    /* Enable 64-bit address mode.  */ +    env->CP0_Status |= (1 << CP0St_UX); +# endif +    /* Enable access to the CPUNum, SYNCI_Step, CC, and CCRes RDHWR +       hardware registers.  */ +    env->CP0_HWREna |= 0x0000000F; +    if (env->CP0_Config1 & (1 << CP0C1_FP)) { +        env->CP0_Status |= (1 << CP0St_CU1); +    } +    if (env->CP0_Config3 & (1 << CP0C3_DSPP)) { +        env->CP0_Status |= (1 << CP0St_MX); +    } +# if defined(TARGET_MIPS64) +    /* For MIPS64, init FR bit to 1 if FPU unit is there and bit is writable. */ +    if ((env->CP0_Config1 & (1 << CP0C1_FP)) && +        (env->CP0_Status_rw_bitmask & (1 << CP0St_FR))) { +        env->CP0_Status |= (1 << CP0St_FR); +    } +# endif +#else +    if (env->hflags & MIPS_HFLAG_BMASK) { +        /* If the exception was raised from a delay slot, +           come back to the jump.  */ +        env->CP0_ErrorEPC = (env->active_tc.PC +                             - (env->hflags & MIPS_HFLAG_B16 ? 2 : 4)); +    } else { +        env->CP0_ErrorEPC = env->active_tc.PC; +    } +    env->active_tc.PC = (int32_t)0xBFC00000; +    env->CP0_Random = env->tlb->nb_tlb - 1; +    env->tlb->tlb_in_use = env->tlb->nb_tlb; +    env->CP0_Wired = 0; +    env->CP0_EBase = (cs->cpu_index & 0x3FF); +    if (kvm_enabled()) { +        env->CP0_EBase |= 0x40000000; +    } else { +        env->CP0_EBase |= 0x80000000; +    } +    env->CP0_Status = (1 << CP0St_BEV) | (1 << CP0St_ERL); +    /* vectored interrupts not implemented, timer on int 7, +       no performance counters. */ +    env->CP0_IntCtl = 0xe0000000; +    { +        int i; + +        for (i = 0; i < 7; i++) { +            env->CP0_WatchLo[i] = 0; +            env->CP0_WatchHi[i] = 0x80000000; +        } +        env->CP0_WatchLo[7] = 0; +        env->CP0_WatchHi[7] = 0; +    } +    /* Count register increments in debug mode, EJTAG version 1 */ +    env->CP0_Debug = (1 << CP0DB_CNT) | (0x1 << CP0DB_VER); + +    cpu_mips_store_count(env, 1); + +    if (env->CP0_Config3 & (1 << CP0C3_MT)) { +        int i; + +        /* Only TC0 on VPE 0 starts as active.  */ +        for (i = 0; i < ARRAY_SIZE(env->tcs); i++) { +            env->tcs[i].CP0_TCBind = cs->cpu_index << CP0TCBd_CurVPE; +            env->tcs[i].CP0_TCHalt = 1; +        } +        env->active_tc.CP0_TCHalt = 1; +        cs->halted = 1; + +        if (cs->cpu_index == 0) { +            /* VPE0 starts up enabled.  */ +            env->mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP); +            env->CP0_VPEConf0 |= (1 << CP0VPEC0_MVP) | (1 << CP0VPEC0_VPA); + +            /* TC0 starts up unhalted.  */ +            cs->halted = 0; +            env->active_tc.CP0_TCHalt = 0; +            env->tcs[0].CP0_TCHalt = 0; +            /* With thread 0 active.  */ +            env->active_tc.CP0_TCStatus = (1 << CP0TCSt_A); +            env->tcs[0].CP0_TCStatus = (1 << CP0TCSt_A); +        } +    } +#endif +    if ((env->insn_flags & ISA_MIPS32R6) && +        (env->active_fpu.fcr0 & (1 << FCR0_F64))) { +        /* Status.FR = 0 mode in 64-bit FPU not allowed in R6 */ +        env->CP0_Status |= (1 << CP0St_FR); +    } + +    /* MSA */ +    if (env->CP0_Config3 & (1 << CP0C3_MSAP)) { +        msa_reset(env); +    } + +    compute_hflags(env); +    restore_rounding_mode(env); +    restore_flush_mode(env); +    restore_pamask(env); +    cs->exception_index = EXCP_NONE; + +    if (semihosting_get_argc()) { +        /* UHI interface can be used to obtain argc and argv */ +        env->active_tc.gpr[4] = -1; +    } +} + +void restore_state_to_opc(CPUMIPSState *env, TranslationBlock *tb, int pc_pos) +{ +    env->active_tc.PC = tcg_ctx.gen_opc_pc[pc_pos]; +    env->hflags &= ~MIPS_HFLAG_BMASK; +    env->hflags |= gen_opc_hflags[pc_pos]; +    switch (env->hflags & MIPS_HFLAG_BMASK_BASE) { +    case MIPS_HFLAG_BR: +        break; +    case MIPS_HFLAG_BC: +    case MIPS_HFLAG_BL: +    case MIPS_HFLAG_B: +        env->btarget = gen_opc_btarget[pc_pos]; +        break; +    } +} diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c new file mode 100644 index 00000000..9304e746 --- /dev/null +++ b/target-mips/translate_init.c @@ -0,0 +1,883 @@ +/* + *  MIPS emulation for qemu: CPU initialisation routines. + * + *  Copyright (c) 2004-2005 Jocelyn Mayer + *  Copyright (c) 2007 Herve Poussineau + * + * 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/>. + */ + +/* CPU / CPU family specific config register values. */ + +/* Have config1, uncached coherency */ +#define MIPS_CONFIG0                                              \ +  ((1U << CP0C0_M) | (0x2 << CP0C0_K0)) + +/* Have config2, no coprocessor2 attached, no MDMX support attached, +   no performance counters, watch registers present, +   no code compression, EJTAG present, no FPU */ +#define MIPS_CONFIG1                                              \ +((1U << CP0C1_M) |                                                \ + (0 << CP0C1_C2) | (0 << CP0C1_MD) | (0 << CP0C1_PC) |            \ + (1 << CP0C1_WR) | (0 << CP0C1_CA) | (1 << CP0C1_EP) |            \ + (0 << CP0C1_FP)) + +/* Have config3, no tertiary/secondary caches implemented */ +#define MIPS_CONFIG2                                              \ +((1U << CP0C2_M)) + +/* No config4, no DSP ASE, no large physaddr (PABITS), +   no external interrupt controller, no vectored interrupts, +   no 1kb pages, no SmartMIPS ASE, no trace logic */ +#define MIPS_CONFIG3                                              \ +((0 << CP0C3_M) | (0 << CP0C3_DSPP) | (0 << CP0C3_LPA) |          \ + (0 << CP0C3_VEIC) | (0 << CP0C3_VInt) | (0 << CP0C3_SP) |        \ + (0 << CP0C3_SM) | (0 << CP0C3_TL)) + +#define MIPS_CONFIG4                                              \ +((0 << CP0C4_M)) + +#define MIPS_CONFIG5                                              \ +((0 << CP0C5_M)) + +/* MMU types, the first four entries have the same layout as the +   CP0C0_MT field.  */ +enum mips_mmu_types { +    MMU_TYPE_NONE, +    MMU_TYPE_R4000, +    MMU_TYPE_RESERVED, +    MMU_TYPE_FMT, +    MMU_TYPE_R3000, +    MMU_TYPE_R6000, +    MMU_TYPE_R8000 +}; + +struct mips_def_t { +    const char *name; +    int32_t CP0_PRid; +    int32_t CP0_Config0; +    int32_t CP0_Config1; +    int32_t CP0_Config2; +    int32_t CP0_Config3; +    int32_t CP0_Config4; +    int32_t CP0_Config4_rw_bitmask; +    int32_t CP0_Config5; +    int32_t CP0_Config5_rw_bitmask; +    int32_t CP0_Config6; +    int32_t CP0_Config7; +    target_ulong CP0_LLAddr_rw_bitmask; +    int CP0_LLAddr_shift; +    int32_t SYNCI_Step; +    int32_t CCRes; +    int32_t CP0_Status_rw_bitmask; +    int32_t CP0_TCStatus_rw_bitmask; +    int32_t CP0_SRSCtl; +    int32_t CP1_fcr0; +    int32_t MSAIR; +    int32_t SEGBITS; +    int32_t PABITS; +    int32_t CP0_SRSConf0_rw_bitmask; +    int32_t CP0_SRSConf0; +    int32_t CP0_SRSConf1_rw_bitmask; +    int32_t CP0_SRSConf1; +    int32_t CP0_SRSConf2_rw_bitmask; +    int32_t CP0_SRSConf2; +    int32_t CP0_SRSConf3_rw_bitmask; +    int32_t CP0_SRSConf3; +    int32_t CP0_SRSConf4_rw_bitmask; +    int32_t CP0_SRSConf4; +    int32_t CP0_PageGrain_rw_bitmask; +    int32_t CP0_PageGrain; +    int insn_flags; +    enum mips_mmu_types mmu_type; +}; + +/*****************************************************************************/ +/* MIPS CPU definitions */ +static const mips_def_t mips_defs[] = +{ +    { +        .name = "4Kc", +        .CP0_PRid = 0x00018000, +        .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (0 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3, +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x1278FF17, +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "4Km", +        .CP0_PRid = 0x00018300, +        /* Config1 implemented, fixed mapping MMU, +           no virtual icache, uncached coherency. */ +        .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_FMT << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3, +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x1258FF17, +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32 | ASE_MIPS16, +        .mmu_type = MMU_TYPE_FMT, +    }, +    { +        .name = "4KEcR1", +        .CP0_PRid = 0x00018400, +        .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (0 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3, +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x1278FF17, +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "4KEmR1", +        .CP0_PRid = 0x00018500, +        .CP0_Config0 = MIPS_CONFIG0 | (MMU_TYPE_FMT << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3, +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x1258FF17, +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32 | ASE_MIPS16, +        .mmu_type = MMU_TYPE_FMT, +    }, +    { +        .name = "4KEc", +        .CP0_PRid = 0x00019000, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | +                    (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (0 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x1278FF17, +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32R2, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "4KEm", +        .CP0_PRid = 0x00019100, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | +                       (MMU_TYPE_FMT << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3, +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x1258FF17, +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32R2 | ASE_MIPS16, +        .mmu_type = MMU_TYPE_FMT, +    }, +    { +        .name = "24Kc", +        .CP0_PRid = 0x00019300, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        /* No DSP implemented. */ +        .CP0_Status_rw_bitmask = 0x1278FF1F, +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32R2 | ASE_MIPS16, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "24Kf", +        .CP0_PRid = 0x00019300, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | +                    (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (0 << CP0C3_VInt), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        /* No DSP implemented. */ +        .CP0_Status_rw_bitmask = 0x3678FF1F, +        .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | +                    (1 << FCR0_D) | (1 << FCR0_S) | (0x93 << FCR0_PRID), +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32R2 | ASE_MIPS16, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "34Kf", +        .CP0_PRid = 0x00019500, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_VInt) | (1 << CP0C3_MT) | +                       (1 << CP0C3_DSPP), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 0, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x3778FF1F, +        .CP0_TCStatus_rw_bitmask = (0 << CP0TCSt_TCU3) | (0 << CP0TCSt_TCU2) | +                    (1 << CP0TCSt_TCU1) | (1 << CP0TCSt_TCU0) | +                    (0 << CP0TCSt_TMX) | (1 << CP0TCSt_DT) | +                    (1 << CP0TCSt_DA) | (1 << CP0TCSt_A) | +                    (0x3 << CP0TCSt_TKSU) | (1 << CP0TCSt_IXMT) | +                    (0xff << CP0TCSt_TASID), +        .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | +                    (1 << FCR0_D) | (1 << FCR0_S) | (0x95 << FCR0_PRID), +        .CP0_SRSCtl = (0xf << CP0SRSCtl_HSS), +        .CP0_SRSConf0_rw_bitmask = 0x3fffffff, +        .CP0_SRSConf0 = (1U << CP0SRSC0_M) | (0x3fe << CP0SRSC0_SRS3) | +                    (0x3fe << CP0SRSC0_SRS2) | (0x3fe << CP0SRSC0_SRS1), +        .CP0_SRSConf1_rw_bitmask = 0x3fffffff, +        .CP0_SRSConf1 = (1U << CP0SRSC1_M) | (0x3fe << CP0SRSC1_SRS6) | +                    (0x3fe << CP0SRSC1_SRS5) | (0x3fe << CP0SRSC1_SRS4), +        .CP0_SRSConf2_rw_bitmask = 0x3fffffff, +        .CP0_SRSConf2 = (1U << CP0SRSC2_M) | (0x3fe << CP0SRSC2_SRS9) | +                    (0x3fe << CP0SRSC2_SRS8) | (0x3fe << CP0SRSC2_SRS7), +        .CP0_SRSConf3_rw_bitmask = 0x3fffffff, +        .CP0_SRSConf3 = (1U << CP0SRSC3_M) | (0x3fe << CP0SRSC3_SRS12) | +                    (0x3fe << CP0SRSC3_SRS11) | (0x3fe << CP0SRSC3_SRS10), +        .CP0_SRSConf4_rw_bitmask = 0x3fffffff, +        .CP0_SRSConf4 = (0x3fe << CP0SRSC4_SRS15) | +                    (0x3fe << CP0SRSC4_SRS14) | (0x3fe << CP0SRSC4_SRS13), +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32R2 | ASE_MIPS16 | ASE_DSP | ASE_MT, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "74Kf", +        .CP0_PRid = 0x00019700, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | +                    (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_DSP2P) | (1 << CP0C3_DSPP) | +                       (1 << CP0C3_VInt), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x3778FF1F, +        .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | +                    (1 << FCR0_D) | (1 << FCR0_S) | (0x93 << FCR0_PRID), +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32R2 | ASE_MIPS16 | ASE_DSP | ASE_DSPR2, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "M14K", +        .CP0_PRid = 0x00019b00, +        /* Config1 implemented, fixed mapping MMU, +           no virtual icache, uncached coherency. */ +        .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_KU) | (0x2 << CP0C0_K23) | +                       (0x1 << CP0C0_AR) | (MMU_TYPE_FMT << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1, +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (1 << CP0C3_VInt), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x1258FF17, +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32R2 | ASE_MICROMIPS, +        .mmu_type = MMU_TYPE_FMT, +    }, +    { +        .name = "M14Kc", +        /* This is the TLB-based MMU core.  */ +        .CP0_PRid = 0x00019c00, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (15 << CP0C1_MMU) | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (0x2 << CP0C3_ISA) | (0 << CP0C3_VInt), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x1278FF17, +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32R2 | ASE_MICROMIPS, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        /* A generic CPU providing MIPS32 Release 5 features. +           FIXME: Eventually this should be replaced by a real CPU model. */ +        .name = "mips32r5-generic", +        .CP0_PRid = 0x00019700, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | +                    (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) | +                       (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_CA), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP) | +                       (1 << CP0C3_LPA), +        .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M), +        .CP0_Config4_rw_bitmask = 0, +        .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_UFR) | (1 << CP0C5_LLB) | +                       (1 << CP0C5_MVH), +        .CP0_Config5_rw_bitmask = (0 << CP0C5_M) | (1 << CP0C5_K) | +                                  (1 << CP0C5_CV) | (0 << CP0C5_EVA) | +                                  (1 << CP0C5_MSAEn) | (1 << CP0C5_UFR) | +                                  (0 << CP0C5_NFExists), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x3778FF1F, +        .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA), +        .CP1_fcr0 = (1 << FCR0_UFRP) | (1 << FCR0_F64) | (1 << FCR0_L) | +                    (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | +                    (0x93 << FCR0_PRID), +        .SEGBITS = 32, +        .PABITS = 40, +        .insn_flags = CPU_MIPS32R5 | ASE_MIPS16 | ASE_MSA, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        /* A generic CPU supporting MIPS32 Release 6 ISA. +           FIXME: Support IEEE 754-2008 FP. +                  Eventually this should be replaced by a real CPU model. */ +        .name = "mips32r6-generic", +        .CP0_PRid = 0x00010000, +        .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AR) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (31 << CP0C1_MMU) | +                       (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) | +                       (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | +                       (0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_BP) | (1 << CP0C3_BI) | +                       (2 << CP0C3_ISA) | (1 << CP0C3_ULRI) | +                       (1 << CP0C3_RXI) | (1U << CP0C3_M), +        .CP0_Config4 = MIPS_CONFIG4 | (0xfc << CP0C4_KScrExist) | +                       (3 << CP0C4_IE) | (1U << CP0C4_M), +        .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_LLB), +        .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | +                                  (1 << CP0C5_UFE), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 0, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x3058FF1F, +        .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | +                         (1U << CP0PG_RIE), +        .CP0_PageGrain_rw_bitmask = 0, +        .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_F64) | (1 << FCR0_L) | +                    (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | +                    (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), +        .SEGBITS = 32, +        .PABITS = 32, +        .insn_flags = CPU_MIPS32R6 | ASE_MICROMIPS, +        .mmu_type = MMU_TYPE_R4000, +    }, +#if defined(TARGET_MIPS64) +    { +        .name = "R4000", +        .CP0_PRid = 0x00000400, +        /* No L2 cache, icache size 8k, dcache size 8k, uncached coherency. */ +        .CP0_Config0 = (1 << 17) | (0x1 << 9) | (0x1 << 6) | (0x2 << CP0C0_K0), +        /* Note: Config1 is only used internally, the R4000 has only Config0. */ +        .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), +        .CP0_LLAddr_rw_bitmask = 0xFFFFFFFF, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 16, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x3678FFFF, +        /* The R4000 has a full 64bit FPU but doesn't use the fcr0 bits. */ +        .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x0 << FCR0_REV), +        .SEGBITS = 40, +        .PABITS = 36, +        .insn_flags = CPU_MIPS3, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "VR5432", +        .CP0_PRid = 0x00005400, +        /* No L2 cache, icache size 8k, dcache size 8k, uncached coherency. */ +        .CP0_Config0 = (1 << 17) | (0x1 << 9) | (0x1 << 6) | (0x2 << CP0C0_K0), +        .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), +        .CP0_LLAddr_rw_bitmask = 0xFFFFFFFFL, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 16, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x3678FFFF, +        /* The VR5432 has a full 64bit FPU but doesn't use the fcr0 bits. */ +        .CP1_fcr0 = (0x54 << FCR0_PRID) | (0x0 << FCR0_REV), +        .SEGBITS = 40, +        .PABITS = 32, +        .insn_flags = CPU_VR54XX, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "5Kc", +        .CP0_PRid = 0x00018100, +        .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AT) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (31 << CP0C1_MMU) | +                       (1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3, +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x12F8FFFF, +        .SEGBITS = 42, +        .PABITS = 36, +        .insn_flags = CPU_MIPS64, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "5Kf", +        .CP0_PRid = 0x00018100, +        .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AT) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (31 << CP0C1_MMU) | +                       (1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3, +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x36F8FFFF, +        /* The 5Kf has F64 / L / W but doesn't use the fcr0 bits. */ +        .CP1_fcr0 = (1 << FCR0_D) | (1 << FCR0_S) | +                    (0x81 << FCR0_PRID) | (0x0 << FCR0_REV), +        .SEGBITS = 42, +        .PABITS = 36, +        .insn_flags = CPU_MIPS64, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "20Kc", +        /* We emulate a later version of the 20Kc, earlier ones had a broken +           WAIT instruction. */ +        .CP0_PRid = 0x000182a0, +        .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AT) | +                    (MMU_TYPE_R4000 << CP0C0_MT) | (1 << CP0C0_VI), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (47 << CP0C1_MMU) | +                       (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) | +                       (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | +                       (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3, +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 0, +        .SYNCI_Step = 32, +        .CCRes = 1, +        .CP0_Status_rw_bitmask = 0x36FBFFFF, +        /* The 20Kc has F64 / L / W but doesn't use the fcr0 bits. */ +        .CP1_fcr0 = (1 << FCR0_3D) | (1 << FCR0_PS) | +                    (1 << FCR0_D) | (1 << FCR0_S) | +                    (0x82 << FCR0_PRID) | (0x0 << FCR0_REV), +        .SEGBITS = 40, +        .PABITS = 36, +        .insn_flags = CPU_MIPS64 | ASE_MIPS3D, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        /* A generic CPU providing MIPS64 Release 2 features. +           FIXME: Eventually this should be replaced by a real CPU model. */ +        .name = "MIPS64R2-generic", +        .CP0_PRid = 0x00010000, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (63 << CP0C1_MMU) | +                       (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) | +                       (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | +                       (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_LPA), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 0, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x36FBFFFF, +        .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_3D) | (1 << FCR0_PS) | +                    (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | +                    (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), +        .SEGBITS = 42, +        .PABITS = 36, +        .insn_flags = CPU_MIPS64R2 | ASE_MIPS3D, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "5KEc", +        .CP0_PRid = 0x00018900, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (31 << CP0C1_MMU) | +                       (1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3, +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x12F8FFFF, +        .SEGBITS = 42, +        .PABITS = 36, +        .insn_flags = CPU_MIPS64R2, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "5KEf", +        .CP0_PRid = 0x00018900, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (31 << CP0C1_MMU) | +                       (1 << CP0C1_IS) | (4 << CP0C1_IL) | (1 << CP0C1_IA) | +                       (1 << CP0C1_DS) | (4 << CP0C1_DL) | (1 << CP0C1_DA) | +                       (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3, +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 4, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x36F8FFFF, +        .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | +                    (1 << FCR0_D) | (1 << FCR0_S) | +                    (0x89 << FCR0_PRID) | (0x0 << FCR0_REV), +        .SEGBITS = 42, +        .PABITS = 36, +        .insn_flags = CPU_MIPS64R2, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        /* A generic CPU supporting MIPS64 Release 6 ISA. +           FIXME: Support IEEE 754-2008 FP. +                  Eventually this should be replaced by a real CPU model. */ +        .name = "MIPS64R6-generic", +        .CP0_PRid = 0x00010000, +        .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AR) | (0x2 << CP0C0_AT) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (63 << CP0C1_MMU) | +                       (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) | +                       (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | +                       (0 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_MSAP) | +                       (1 << CP0C3_BP) | (1 << CP0C3_BI) | (1 << CP0C3_ULRI) | +                       (1 << CP0C3_RXI) | (1 << CP0C3_LPA), +        .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | +                       (0xfc << CP0C4_KScrExist), +        .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_LLB), +        .CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) | +                                  (1 << CP0C5_FRE) | (1 << CP0C5_UFE), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 0, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x30D8FFFF, +        .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | +                         (1U << CP0PG_RIE), +        .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA), +        .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_F64) | (1 << FCR0_L) | +                    (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | +                    (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), +        .SEGBITS = 48, +        .PABITS = 48, +        .insn_flags = CPU_MIPS64R6 | ASE_MSA, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "Loongson-2E", +        .CP0_PRid = 0x6302, +        /* 64KB I-cache and d-cache. 4 way with 32 bit cache line size.  */ +        .CP0_Config0 = (0x1<<17) | (0x1<<16) | (0x1<<11) | (0x1<<8) | +                       (0x1<<5) | (0x1<<4) | (0x1<<1), +        /* Note: Config1 is only used internally, +           Loongson-2E has only Config0.  */ +        .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), +        .SYNCI_Step = 16, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x35D0FFFF, +        .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV), +        .SEGBITS = 40, +        .PABITS = 40, +        .insn_flags = CPU_LOONGSON2E, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        .name = "Loongson-2F", +        .CP0_PRid = 0x6303, +        /* 64KB I-cache and d-cache. 4 way with 32 bit cache line size.  */ +        .CP0_Config0 = (0x1<<17) | (0x1<<16) | (0x1<<11) | (0x1<<8) | +                       (0x1<<5) | (0x1<<4) | (0x1<<1), +        /* Note: Config1 is only used internally, +           Loongson-2F has only Config0.  */ +        .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), +        .SYNCI_Step = 16, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0xF5D0FF1F,   /* Bits 7:5 not writable.  */ +        .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV), +        .SEGBITS = 40, +        .PABITS = 40, +        .insn_flags = CPU_LOONGSON2F, +        .mmu_type = MMU_TYPE_R4000, +    }, +    { +        /* A generic CPU providing MIPS64 ASE DSP 2 features. +           FIXME: Eventually this should be replaced by a real CPU model. */ +        .name = "mips64dspr2", +        .CP0_PRid = 0x00010000, +        .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | (0x2 << CP0C0_AT) | +                       (MMU_TYPE_R4000 << CP0C0_MT), +        .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (63 << CP0C1_MMU) | +                       (2 << CP0C1_IS) | (4 << CP0C1_IL) | (3 << CP0C1_IA) | +                       (2 << CP0C1_DS) | (4 << CP0C1_DL) | (3 << CP0C1_DA) | +                       (1 << CP0C1_PC) | (1 << CP0C1_WR) | (1 << CP0C1_EP), +        .CP0_Config2 = MIPS_CONFIG2, +        .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_DSP2P) | +                       (1 << CP0C3_DSPP) | (1 << CP0C3_LPA), +        .CP0_LLAddr_rw_bitmask = 0, +        .CP0_LLAddr_shift = 0, +        .SYNCI_Step = 32, +        .CCRes = 2, +        .CP0_Status_rw_bitmask = 0x37FBFFFF, +        .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_3D) | (1 << FCR0_PS) | +                    (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | +                    (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV), +        .SEGBITS = 42, +        .PABITS = 36, +        .insn_flags = CPU_MIPS64R2 | ASE_DSP | ASE_DSPR2, +        .mmu_type = MMU_TYPE_R4000, +    }, + +#endif +}; + +static const mips_def_t *cpu_mips_find_by_name (const char *name) +{ +    int i; + +    for (i = 0; i < ARRAY_SIZE(mips_defs); i++) { +        if (strcasecmp(name, mips_defs[i].name) == 0) { +            return &mips_defs[i]; +        } +    } +    return NULL; +} + +void mips_cpu_list (FILE *f, fprintf_function cpu_fprintf) +{ +    int i; + +    for (i = 0; i < ARRAY_SIZE(mips_defs); i++) { +        (*cpu_fprintf)(f, "MIPS '%s'\n", +                       mips_defs[i].name); +    } +} + +#ifndef CONFIG_USER_ONLY +static void no_mmu_init (CPUMIPSState *env, const mips_def_t *def) +{ +    env->tlb->nb_tlb = 1; +    env->tlb->map_address = &no_mmu_map_address; +} + +static void fixed_mmu_init (CPUMIPSState *env, const mips_def_t *def) +{ +    env->tlb->nb_tlb = 1; +    env->tlb->map_address = &fixed_mmu_map_address; +} + +static void r4k_mmu_init (CPUMIPSState *env, const mips_def_t *def) +{ +    env->tlb->nb_tlb = 1 + ((def->CP0_Config1 >> CP0C1_MMU) & 63); +    env->tlb->map_address = &r4k_map_address; +    env->tlb->helper_tlbwi = r4k_helper_tlbwi; +    env->tlb->helper_tlbwr = r4k_helper_tlbwr; +    env->tlb->helper_tlbp = r4k_helper_tlbp; +    env->tlb->helper_tlbr = r4k_helper_tlbr; +    env->tlb->helper_tlbinv = r4k_helper_tlbinv; +    env->tlb->helper_tlbinvf = r4k_helper_tlbinvf; +} + +static void mmu_init (CPUMIPSState *env, const mips_def_t *def) +{ +    MIPSCPU *cpu = mips_env_get_cpu(env); + +    env->tlb = g_malloc0(sizeof(CPUMIPSTLBContext)); + +    switch (def->mmu_type) { +        case MMU_TYPE_NONE: +            no_mmu_init(env, def); +            break; +        case MMU_TYPE_R4000: +            r4k_mmu_init(env, def); +            break; +        case MMU_TYPE_FMT: +            fixed_mmu_init(env, def); +            break; +        case MMU_TYPE_R3000: +        case MMU_TYPE_R6000: +        case MMU_TYPE_R8000: +        default: +            cpu_abort(CPU(cpu), "MMU type not supported\n"); +    } +} +#endif /* CONFIG_USER_ONLY */ + +static void fpu_init (CPUMIPSState *env, const mips_def_t *def) +{ +    int i; + +    for (i = 0; i < MIPS_FPU_MAX; i++) +        env->fpus[i].fcr0 = def->CP1_fcr0; + +    memcpy(&env->active_fpu, &env->fpus[0], sizeof(env->active_fpu)); +} + +static void mvp_init (CPUMIPSState *env, const mips_def_t *def) +{ +    env->mvp = g_malloc0(sizeof(CPUMIPSMVPContext)); + +    /* MVPConf1 implemented, TLB sharable, no gating storage support, +       programmable cache partitioning implemented, number of allocatable +       and sharable TLB entries, MVP has allocatable TCs, 2 VPEs +       implemented, 5 TCs implemented. */ +    env->mvp->CP0_MVPConf0 = (1U << CP0MVPC0_M) | (1 << CP0MVPC0_TLBS) | +                             (0 << CP0MVPC0_GS) | (1 << CP0MVPC0_PCP) | +// TODO: actually do 2 VPEs. +//                             (1 << CP0MVPC0_TCA) | (0x1 << CP0MVPC0_PVPE) | +//                             (0x04 << CP0MVPC0_PTC); +                             (1 << CP0MVPC0_TCA) | (0x0 << CP0MVPC0_PVPE) | +                             (0x00 << CP0MVPC0_PTC); +#if !defined(CONFIG_USER_ONLY) +    /* Usermode has no TLB support */ +    env->mvp->CP0_MVPConf0 |= (env->tlb->nb_tlb << CP0MVPC0_PTLBE); +#endif + +    /* Allocatable CP1 have media extensions, allocatable CP1 have FP support, +       no UDI implemented, no CP2 implemented, 1 CP1 implemented. */ +    env->mvp->CP0_MVPConf1 = (1U << CP0MVPC1_CIM) | (1 << CP0MVPC1_CIF) | +                             (0x0 << CP0MVPC1_PCX) | (0x0 << CP0MVPC1_PCP2) | +                             (0x1 << CP0MVPC1_PCP1); +} + +static void msa_reset(CPUMIPSState *env) +{ +#ifdef CONFIG_USER_ONLY +    /* MSA access enabled */ +    env->CP0_Config5 |= 1 << CP0C5_MSAEn; +    env->CP0_Status |= (1 << CP0St_CU1) | (1 << CP0St_FR); +#endif + +    /* MSA CSR: +       - non-signaling floating point exception mode off (NX bit is 0) +       - Cause, Enables, and Flags are all 0 +       - round to nearest / ties to even (RM bits are 0) */ +    env->active_tc.msacsr = 0; + +    restore_msa_fp_status(env); + +    /* tininess detected after rounding.*/ +    set_float_detect_tininess(float_tininess_after_rounding, +                              &env->active_tc.msa_fp_status); + +    /* clear float_status exception flags */ +    set_float_exception_flags(0, &env->active_tc.msa_fp_status); + +    /* clear float_status nan mode */ +    set_default_nan_mode(0, &env->active_tc.msa_fp_status); +}  | 
