diff options
Diffstat (limited to 'target-sparc')
| -rw-r--r-- | target-sparc/Makefile.objs | 7 | ||||
| -rw-r--r-- | target-sparc/TODO | 88 | ||||
| -rw-r--r-- | target-sparc/cc_helper.c | 485 | ||||
| -rw-r--r-- | target-sparc/cpu-qom.h | 88 | ||||
| -rw-r--r-- | target-sparc/cpu.c | 882 | ||||
| -rw-r--r-- | target-sparc/cpu.h | 747 | ||||
| -rw-r--r-- | target-sparc/fop_helper.c | 467 | ||||
| -rw-r--r-- | target-sparc/gdbstub.c | 208 | ||||
| -rw-r--r-- | target-sparc/helper.c | 255 | ||||
| -rw-r--r-- | target-sparc/helper.h | 175 | ||||
| -rw-r--r-- | target-sparc/int32_helper.c | 173 | ||||
| -rw-r--r-- | target-sparc/int64_helper.c | 204 | ||||
| -rw-r--r-- | target-sparc/ldst_helper.c | 2455 | ||||
| -rw-r--r-- | target-sparc/machine.c | 218 | ||||
| -rw-r--r-- | target-sparc/mmu_helper.c | 867 | ||||
| -rw-r--r-- | target-sparc/translate.c | 5471 | ||||
| -rw-r--r-- | target-sparc/vis_helper.c | 489 | ||||
| -rw-r--r-- | target-sparc/win_helper.c | 393 | 
18 files changed, 13672 insertions, 0 deletions
diff --git a/target-sparc/Makefile.objs b/target-sparc/Makefile.objs new file mode 100644 index 00000000..1cd81ccc --- /dev/null +++ b/target-sparc/Makefile.objs @@ -0,0 +1,7 @@ +obj-$(CONFIG_SOFTMMU) += machine.o +obj-y += translate.o helper.o cpu.o +obj-y += fop_helper.o cc_helper.o win_helper.o mmu_helper.o ldst_helper.o +obj-$(TARGET_SPARC) += int32_helper.o +obj-$(TARGET_SPARC64) += int64_helper.o +obj-$(TARGET_SPARC64) += vis_helper.o +obj-y += gdbstub.o diff --git a/target-sparc/TODO b/target-sparc/TODO new file mode 100644 index 00000000..b8c727e8 --- /dev/null +++ b/target-sparc/TODO @@ -0,0 +1,88 @@ +TODO-list: + +CPU common: +- Unimplemented features/bugs: + - Delay slot handling may fail sometimes (branch end of page, delay + slot next page) + - Atomical instructions + - CPU features should match real CPUs (also ASI selection) +- Optimizations/improvements: + - Condition code/branch handling like x86, also for FPU? + - Remove remaining explicit alignment checks + - Global register for regwptr, so that windowed registers can be + accessed directly + - Improve Sparc32plus addressing + - NPC/PC static optimisations (use JUMP_TB when possible)? (Is this + obsolete?) + - Synthetic instructions + - MMU model dependent on CPU model + - Select ASI helper at translation time (on V9 only if known) + - KQemu/KVM support for VM only + - Hardware breakpoint/watchpoint support + - Cache emulation mode + - Reverse-endian pages + - Faster FPU emulation + - Busy loop detection + +Sparc32 CPUs: +- Unimplemented features/bugs: + - Sun4/Sun4c MMUs + - Some V8 ASIs + +Sparc64 CPUs: +- Unimplemented features/bugs: + - Interrupt handling + - Secondary address space, other MMU functions + - Many V9/UA2005/UA2007 ASIs + - Rest of V9 instructions, missing VIS instructions + - IG/MG/AG vs. UA2007 globals + - Full hypervisor support + - SMP/CMT + - Sun4v CPUs + +Sun4: +- To be added + +Sun4c: +- A lot of unimplemented features +- Maybe split from Sun4m + +Sun4m: +- Unimplemented features/bugs: + - Hardware devices do not match real boards + - Floppy does not work + - CS4231: merge with cs4231a, add DMA + - Add cg6, bwtwo + - Arbitrary resolution support + - PCI for MicroSparc-IIe + - JavaStation machines + - SBus slot probing, FCode ROM support + - SMP probing support + - Interrupt routing does not match real HW + - SuSE 7.3 keyboard sometimes unresponsive + - Gentoo 2004.1 SMP does not work + - SS600MP ledma -> lebuffer + - Type 5 keyboard + - Less fixed hardware choices + - DBRI audio (Am7930) + - BPP parallel + - Diagnostic switch + - ESP PIO mode + +Sun4d: +- A lot of unimplemented features: + - SBI + - IO-unit +- Maybe split from Sun4m + +Sun4u: +- Unimplemented features/bugs: + - Interrupt controller + - PCI/IOMMU support (Simba, JIO, Tomatillo, Psycho, Schizo, Safari...) + - SMP + - Happy Meal Ethernet, flash, I2C, GPIO + - A lot of real machine types + +Sun4v: +- A lot of unimplemented features + - A lot of real machine types diff --git a/target-sparc/cc_helper.c b/target-sparc/cc_helper.c new file mode 100644 index 00000000..35dab732 --- /dev/null +++ b/target-sparc/cc_helper.c @@ -0,0 +1,485 @@ +/* + * Helpers for lazy condition code handling + * + *  Copyright (c) 2003-2005 Fabrice Bellard + * + * 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" + +static uint32_t compute_all_flags(CPUSPARCState *env) +{ +    return env->psr & PSR_ICC; +} + +static uint32_t compute_C_flags(CPUSPARCState *env) +{ +    return env->psr & PSR_CARRY; +} + +static inline uint32_t get_NZ_icc(int32_t dst) +{ +    uint32_t ret = 0; + +    if (dst == 0) { +        ret = PSR_ZERO; +    } else if (dst < 0) { +        ret = PSR_NEG; +    } +    return ret; +} + +#ifdef TARGET_SPARC64 +static uint32_t compute_all_flags_xcc(CPUSPARCState *env) +{ +    return env->xcc & PSR_ICC; +} + +static uint32_t compute_C_flags_xcc(CPUSPARCState *env) +{ +    return env->xcc & PSR_CARRY; +} + +static inline uint32_t get_NZ_xcc(target_long dst) +{ +    uint32_t ret = 0; + +    if (!dst) { +        ret = PSR_ZERO; +    } else if (dst < 0) { +        ret = PSR_NEG; +    } +    return ret; +} +#endif + +static inline uint32_t get_V_div_icc(target_ulong src2) +{ +    uint32_t ret = 0; + +    if (src2 != 0) { +        ret = PSR_OVF; +    } +    return ret; +} + +static uint32_t compute_all_div(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_icc(CC_DST); +    ret |= get_V_div_icc(CC_SRC2); +    return ret; +} + +static uint32_t compute_C_div(CPUSPARCState *env) +{ +    return 0; +} + +static inline uint32_t get_C_add_icc(uint32_t dst, uint32_t src1) +{ +    uint32_t ret = 0; + +    if (dst < src1) { +        ret = PSR_CARRY; +    } +    return ret; +} + +static inline uint32_t get_C_addx_icc(uint32_t dst, uint32_t src1, +                                      uint32_t src2) +{ +    uint32_t ret = 0; + +    if (((src1 & src2) | (~dst & (src1 | src2))) & (1U << 31)) { +        ret = PSR_CARRY; +    } +    return ret; +} + +static inline uint32_t get_V_add_icc(uint32_t dst, uint32_t src1, +                                     uint32_t src2) +{ +    uint32_t ret = 0; + +    if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1U << 31)) { +        ret = PSR_OVF; +    } +    return ret; +} + +#ifdef TARGET_SPARC64 +static inline uint32_t get_C_add_xcc(target_ulong dst, target_ulong src1) +{ +    uint32_t ret = 0; + +    if (dst < src1) { +        ret = PSR_CARRY; +    } +    return ret; +} + +static inline uint32_t get_C_addx_xcc(target_ulong dst, target_ulong src1, +                                      target_ulong src2) +{ +    uint32_t ret = 0; + +    if (((src1 & src2) | (~dst & (src1 | src2))) & (1ULL << 63)) { +        ret = PSR_CARRY; +    } +    return ret; +} + +static inline uint32_t get_V_add_xcc(target_ulong dst, target_ulong src1, +                                     target_ulong src2) +{ +    uint32_t ret = 0; + +    if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1ULL << 63)) { +        ret = PSR_OVF; +    } +    return ret; +} + +static uint32_t compute_all_add_xcc(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_xcc(CC_DST); +    ret |= get_C_add_xcc(CC_DST, CC_SRC); +    ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_C_add_xcc(CPUSPARCState *env) +{ +    return get_C_add_xcc(CC_DST, CC_SRC); +} +#endif + +static uint32_t compute_all_add(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_icc(CC_DST); +    ret |= get_C_add_icc(CC_DST, CC_SRC); +    ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_C_add(CPUSPARCState *env) +{ +    return get_C_add_icc(CC_DST, CC_SRC); +} + +#ifdef TARGET_SPARC64 +static uint32_t compute_all_addx_xcc(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_xcc(CC_DST); +    ret |= get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2); +    ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_C_addx_xcc(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} +#endif + +static uint32_t compute_all_addx(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_icc(CC_DST); +    ret |= get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2); +    ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_C_addx(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} + +static inline uint32_t get_V_tag_icc(target_ulong src1, target_ulong src2) +{ +    uint32_t ret = 0; + +    if ((src1 | src2) & 0x3) { +        ret = PSR_OVF; +    } +    return ret; +} + +static uint32_t compute_all_tadd(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_icc(CC_DST); +    ret |= get_C_add_icc(CC_DST, CC_SRC); +    ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2); +    ret |= get_V_tag_icc(CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_all_taddtv(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_icc(CC_DST); +    ret |= get_C_add_icc(CC_DST, CC_SRC); +    return ret; +} + +static inline uint32_t get_C_sub_icc(uint32_t src1, uint32_t src2) +{ +    uint32_t ret = 0; + +    if (src1 < src2) { +        ret = PSR_CARRY; +    } +    return ret; +} + +static inline uint32_t get_C_subx_icc(uint32_t dst, uint32_t src1, +                                      uint32_t src2) +{ +    uint32_t ret = 0; + +    if (((~src1 & src2) | (dst & (~src1 | src2))) & (1U << 31)) { +        ret = PSR_CARRY; +    } +    return ret; +} + +static inline uint32_t get_V_sub_icc(uint32_t dst, uint32_t src1, +                                     uint32_t src2) +{ +    uint32_t ret = 0; + +    if (((src1 ^ src2) & (src1 ^ dst)) & (1U << 31)) { +        ret = PSR_OVF; +    } +    return ret; +} + + +#ifdef TARGET_SPARC64 +static inline uint32_t get_C_sub_xcc(target_ulong src1, target_ulong src2) +{ +    uint32_t ret = 0; + +    if (src1 < src2) { +        ret = PSR_CARRY; +    } +    return ret; +} + +static inline uint32_t get_C_subx_xcc(target_ulong dst, target_ulong src1, +                                      target_ulong src2) +{ +    uint32_t ret = 0; + +    if (((~src1 & src2) | (dst & (~src1 | src2))) & (1ULL << 63)) { +        ret = PSR_CARRY; +    } +    return ret; +} + +static inline uint32_t get_V_sub_xcc(target_ulong dst, target_ulong src1, +                                     target_ulong src2) +{ +    uint32_t ret = 0; + +    if (((src1 ^ src2) & (src1 ^ dst)) & (1ULL << 63)) { +        ret = PSR_OVF; +    } +    return ret; +} + +static uint32_t compute_all_sub_xcc(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_xcc(CC_DST); +    ret |= get_C_sub_xcc(CC_SRC, CC_SRC2); +    ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_C_sub_xcc(CPUSPARCState *env) +{ +    return get_C_sub_xcc(CC_SRC, CC_SRC2); +} +#endif + +static uint32_t compute_all_sub(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_icc(CC_DST); +    ret |= get_C_sub_icc(CC_SRC, CC_SRC2); +    ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_C_sub(CPUSPARCState *env) +{ +    return get_C_sub_icc(CC_SRC, CC_SRC2); +} + +#ifdef TARGET_SPARC64 +static uint32_t compute_all_subx_xcc(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_xcc(CC_DST); +    ret |= get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2); +    ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_C_subx_xcc(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} +#endif + +static uint32_t compute_all_subx(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_icc(CC_DST); +    ret |= get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2); +    ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_C_subx(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_all_tsub(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_icc(CC_DST); +    ret |= get_C_sub_icc(CC_SRC, CC_SRC2); +    ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2); +    ret |= get_V_tag_icc(CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_all_tsubtv(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = get_NZ_icc(CC_DST); +    ret |= get_C_sub_icc(CC_SRC, CC_SRC2); +    return ret; +} + +static uint32_t compute_all_logic(CPUSPARCState *env) +{ +    return get_NZ_icc(CC_DST); +} + +static uint32_t compute_C_logic(CPUSPARCState *env) +{ +    return 0; +} + +#ifdef TARGET_SPARC64 +static uint32_t compute_all_logic_xcc(CPUSPARCState *env) +{ +    return get_NZ_xcc(CC_DST); +} +#endif + +typedef struct CCTable { +    uint32_t (*compute_all)(CPUSPARCState *env); /* return all the flags */ +    uint32_t (*compute_c)(CPUSPARCState *env);  /* return the C flag */ +} CCTable; + +static const CCTable icc_table[CC_OP_NB] = { +    /* CC_OP_DYNAMIC should never happen */ +    [CC_OP_FLAGS] = { compute_all_flags, compute_C_flags }, +    [CC_OP_DIV] = { compute_all_div, compute_C_div }, +    [CC_OP_ADD] = { compute_all_add, compute_C_add }, +    [CC_OP_ADDX] = { compute_all_addx, compute_C_addx }, +    [CC_OP_TADD] = { compute_all_tadd, compute_C_add }, +    [CC_OP_TADDTV] = { compute_all_taddtv, compute_C_add }, +    [CC_OP_SUB] = { compute_all_sub, compute_C_sub }, +    [CC_OP_SUBX] = { compute_all_subx, compute_C_subx }, +    [CC_OP_TSUB] = { compute_all_tsub, compute_C_sub }, +    [CC_OP_TSUBTV] = { compute_all_tsubtv, compute_C_sub }, +    [CC_OP_LOGIC] = { compute_all_logic, compute_C_logic }, +}; + +#ifdef TARGET_SPARC64 +static const CCTable xcc_table[CC_OP_NB] = { +    /* CC_OP_DYNAMIC should never happen */ +    [CC_OP_FLAGS] = { compute_all_flags_xcc, compute_C_flags_xcc }, +    [CC_OP_DIV] = { compute_all_logic_xcc, compute_C_logic }, +    [CC_OP_ADD] = { compute_all_add_xcc, compute_C_add_xcc }, +    [CC_OP_ADDX] = { compute_all_addx_xcc, compute_C_addx_xcc }, +    [CC_OP_TADD] = { compute_all_add_xcc, compute_C_add_xcc }, +    [CC_OP_TADDTV] = { compute_all_add_xcc, compute_C_add_xcc }, +    [CC_OP_SUB] = { compute_all_sub_xcc, compute_C_sub_xcc }, +    [CC_OP_SUBX] = { compute_all_subx_xcc, compute_C_subx_xcc }, +    [CC_OP_TSUB] = { compute_all_sub_xcc, compute_C_sub_xcc }, +    [CC_OP_TSUBTV] = { compute_all_sub_xcc, compute_C_sub_xcc }, +    [CC_OP_LOGIC] = { compute_all_logic_xcc, compute_C_logic }, +}; +#endif + +void helper_compute_psr(CPUSPARCState *env) +{ +    uint32_t new_psr; + +    new_psr = icc_table[CC_OP].compute_all(env); +    env->psr = new_psr; +#ifdef TARGET_SPARC64 +    new_psr = xcc_table[CC_OP].compute_all(env); +    env->xcc = new_psr; +#endif +    CC_OP = CC_OP_FLAGS; +} + +uint32_t helper_compute_C_icc(CPUSPARCState *env) +{ +    uint32_t ret; + +    ret = icc_table[CC_OP].compute_c(env) >> PSR_CARRY_SHIFT; +    return ret; +} diff --git a/target-sparc/cpu-qom.h b/target-sparc/cpu-qom.h new file mode 100644 index 00000000..477c4d51 --- /dev/null +++ b/target-sparc/cpu-qom.h @@ -0,0 +1,88 @@ +/* + * QEMU SPARC 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_SPARC_CPU_QOM_H +#define QEMU_SPARC_CPU_QOM_H + +#include "qom/cpu.h" +#include "cpu.h" + +#ifdef TARGET_SPARC64 +#define TYPE_SPARC_CPU "sparc64-cpu" +#else +#define TYPE_SPARC_CPU "sparc-cpu" +#endif + +#define SPARC_CPU_CLASS(klass) \ +    OBJECT_CLASS_CHECK(SPARCCPUClass, (klass), TYPE_SPARC_CPU) +#define SPARC_CPU(obj) \ +    OBJECT_CHECK(SPARCCPU, (obj), TYPE_SPARC_CPU) +#define SPARC_CPU_GET_CLASS(obj) \ +    OBJECT_GET_CLASS(SPARCCPUClass, (obj), TYPE_SPARC_CPU) + +/** + * SPARCCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A SPARC CPU model. + */ +typedef struct SPARCCPUClass { +    /*< private >*/ +    CPUClass parent_class; +    /*< public >*/ + +    DeviceRealize parent_realize; +    void (*parent_reset)(CPUState *cpu); +} SPARCCPUClass; + +/** + * SPARCCPU: + * @env: #CPUSPARCState + * + * A SPARC CPU. + */ +typedef struct SPARCCPU { +    /*< private >*/ +    CPUState parent_obj; +    /*< public >*/ + +    CPUSPARCState env; +} SPARCCPU; + +static inline SPARCCPU *sparc_env_get_cpu(CPUSPARCState *env) +{ +    return container_of(env, SPARCCPU, env); +} + +#define ENV_GET_CPU(e) CPU(sparc_env_get_cpu(e)) + +#define ENV_OFFSET offsetof(SPARCCPU, env) + +void sparc_cpu_do_interrupt(CPUState *cpu); +void sparc_cpu_dump_state(CPUState *cpu, FILE *f, +                          fprintf_function cpu_fprintf, int flags); +hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int sparc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int sparc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cpu, +                                                 vaddr addr, int is_write, +                                                 int is_user, uintptr_t retaddr); + +#endif diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c new file mode 100644 index 00000000..82bb72ab --- /dev/null +++ b/target-sparc/cpu.c @@ -0,0 +1,882 @@ +/* + * Sparc CPU init helpers + * + *  Copyright (c) 2003-2005 Fabrice Bellard + * + * 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 "qemu/error-report.h" + +//#define DEBUG_FEATURES + +static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model); + +/* CPUClass::reset() */ +static void sparc_cpu_reset(CPUState *s) +{ +    SPARCCPU *cpu = SPARC_CPU(s); +    SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(cpu); +    CPUSPARCState *env = &cpu->env; + +    scc->parent_reset(s); + +    memset(env, 0, offsetof(CPUSPARCState, version)); +    tlb_flush(s, 1); +    env->cwp = 0; +#ifndef TARGET_SPARC64 +    env->wim = 1; +#endif +    env->regwptr = env->regbase + (env->cwp * 16); +    CC_OP = CC_OP_FLAGS; +#if defined(CONFIG_USER_ONLY) +#ifdef TARGET_SPARC64 +    env->cleanwin = env->nwindows - 2; +    env->cansave = env->nwindows - 2; +    env->pstate = PS_RMO | PS_PEF | PS_IE; +    env->asi = 0x82; /* Primary no-fault */ +#endif +#else +#if !defined(TARGET_SPARC64) +    env->psret = 0; +    env->psrs = 1; +    env->psrps = 1; +#endif +#ifdef TARGET_SPARC64 +    env->pstate = PS_PRIV|PS_RED|PS_PEF|PS_AG; +    env->hpstate = cpu_has_hypervisor(env) ? HS_PRIV : 0; +    env->tl = env->maxtl; +    cpu_tsptr(env)->tt = TT_POWER_ON_RESET; +    env->lsu = 0; +#else +    env->mmuregs[0] &= ~(MMU_E | MMU_NF); +    env->mmuregs[0] |= env->def->mmu_bm; +#endif +    env->pc = 0; +    env->npc = env->pc + 4; +#endif +    env->cache_control = 0; +} + +static bool sparc_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ +    if (interrupt_request & CPU_INTERRUPT_HARD) { +        SPARCCPU *cpu = SPARC_CPU(cs); +        CPUSPARCState *env = &cpu->env; + +        if (cpu_interrupts_enabled(env) && env->interrupt_index > 0) { +            int pil = env->interrupt_index & 0xf; +            int type = env->interrupt_index & 0xf0; + +            if (type != TT_EXTINT || cpu_pil_allowed(env, pil)) { +                cs->exception_index = env->interrupt_index; +                sparc_cpu_do_interrupt(cs); +                return true; +            } +        } +    } +    return false; +} + +static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model) +{ +    CPUClass *cc = CPU_GET_CLASS(cpu); +    CPUSPARCState *env = &cpu->env; +    char *s = g_strdup(cpu_model); +    char *featurestr, *name = strtok(s, ","); +    sparc_def_t def1, *def = &def1; +    Error *err = NULL; + +    if (cpu_sparc_find_by_name(def, name) < 0) { +        g_free(s); +        return -1; +    } + +    env->def = g_new0(sparc_def_t, 1); +    memcpy(env->def, def, sizeof(*def)); + +    featurestr = strtok(NULL, ","); +    cc->parse_features(CPU(cpu), featurestr, &err); +    g_free(s); +    if (err) { +        error_report_err(err); +        return -1; +    } + +    env->version = def->iu_version; +    env->fsr = def->fpu_version; +    env->nwindows = def->nwindows; +#if !defined(TARGET_SPARC64) +    env->mmuregs[0] |= def->mmu_version; +    cpu_sparc_set_id(env, 0); +    env->mxccregs[7] |= def->mxcc_version; +#else +    env->mmu_version = def->mmu_version; +    env->maxtl = def->maxtl; +    env->version |= def->maxtl << 8; +    env->version |= def->nwindows - 1; +#endif +    return 0; +} + +SPARCCPU *cpu_sparc_init(const char *cpu_model) +{ +    SPARCCPU *cpu; + +    cpu = SPARC_CPU(object_new(TYPE_SPARC_CPU)); + +    if (cpu_sparc_register(cpu, cpu_model) < 0) { +        object_unref(OBJECT(cpu)); +        return NULL; +    } + +    object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + +    return cpu; +} + +void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu) +{ +#if !defined(TARGET_SPARC64) +    env->mxccregs[7] = ((cpu + 8) & 0xf) << 24; +#endif +} + +static const sparc_def_t sparc_defs[] = { +#ifdef TARGET_SPARC64 +    { +        .name = "Fujitsu Sparc64", +        .iu_version = ((0x04ULL << 48) | (0x02ULL << 32) | (0ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 4, +        .maxtl = 4, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "Fujitsu Sparc64 III", +        .iu_version = ((0x04ULL << 48) | (0x03ULL << 32) | (0ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 5, +        .maxtl = 4, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "Fujitsu Sparc64 IV", +        .iu_version = ((0x04ULL << 48) | (0x04ULL << 32) | (0ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "Fujitsu Sparc64 V", +        .iu_version = ((0x04ULL << 48) | (0x05ULL << 32) | (0x51ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI UltraSparc I", +        .iu_version = ((0x17ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI UltraSparc II", +        .iu_version = ((0x17ULL << 48) | (0x11ULL << 32) | (0x20ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI UltraSparc IIi", +        .iu_version = ((0x17ULL << 48) | (0x12ULL << 32) | (0x91ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI UltraSparc IIe", +        .iu_version = ((0x17ULL << 48) | (0x13ULL << 32) | (0x14ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "Sun UltraSparc III", +        .iu_version = ((0x3eULL << 48) | (0x14ULL << 32) | (0x34ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "Sun UltraSparc III Cu", +        .iu_version = ((0x3eULL << 48) | (0x15ULL << 32) | (0x41ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_3, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "Sun UltraSparc IIIi", +        .iu_version = ((0x3eULL << 48) | (0x16ULL << 32) | (0x34ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "Sun UltraSparc IV", +        .iu_version = ((0x3eULL << 48) | (0x18ULL << 32) | (0x31ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_4, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "Sun UltraSparc IV+", +        .iu_version = ((0x3eULL << 48) | (0x19ULL << 32) | (0x22ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_CMT, +    }, +    { +        .name = "Sun UltraSparc IIIi+", +        .iu_version = ((0x3eULL << 48) | (0x22ULL << 32) | (0ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_3, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "Sun UltraSparc T1", +        /* defined in sparc_ifu_fdp.v and ctu.h */ +        .iu_version = ((0x3eULL << 48) | (0x23ULL << 32) | (0x02ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_sun4v, +        .nwindows = 8, +        .maxtl = 6, +        .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_HYPV | CPU_FEATURE_CMT +        | CPU_FEATURE_GL, +    }, +    { +        .name = "Sun UltraSparc T2", +        /* defined in tlu_asi_ctl.v and n2_revid_cust.v */ +        .iu_version = ((0x3eULL << 48) | (0x24ULL << 32) | (0x02ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_sun4v, +        .nwindows = 8, +        .maxtl = 6, +        .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_HYPV | CPU_FEATURE_CMT +        | CPU_FEATURE_GL, +    }, +    { +        .name = "NEC UltraSparc I", +        .iu_version = ((0x22ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)), +        .fpu_version = 0x00000000, +        .mmu_version = mmu_us_12, +        .nwindows = 8, +        .maxtl = 5, +        .features = CPU_DEFAULT_FEATURES, +    }, +#else +    { +        .name = "Fujitsu MB86904", +        .iu_version = 0x04 << 24, /* Impl 0, ver 4 */ +        .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ +        .mmu_version = 0x04 << 24, /* Impl 0, ver 4 */ +        .mmu_bm = 0x00004000, +        .mmu_ctpr_mask = 0x00ffffc0, +        .mmu_cxr_mask = 0x000000ff, +        .mmu_sfsr_mask = 0x00016fff, +        .mmu_trcr_mask = 0x00ffffff, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "Fujitsu MB86907", +        .iu_version = 0x05 << 24, /* Impl 0, ver 5 */ +        .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ +        .mmu_version = 0x05 << 24, /* Impl 0, ver 5 */ +        .mmu_bm = 0x00004000, +        .mmu_ctpr_mask = 0xffffffc0, +        .mmu_cxr_mask = 0x000000ff, +        .mmu_sfsr_mask = 0x00016fff, +        .mmu_trcr_mask = 0xffffffff, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI MicroSparc I", +        .iu_version = 0x41000000, +        .fpu_version = 4 << 17, +        .mmu_version = 0x41000000, +        .mmu_bm = 0x00004000, +        .mmu_ctpr_mask = 0x007ffff0, +        .mmu_cxr_mask = 0x0000003f, +        .mmu_sfsr_mask = 0x00016fff, +        .mmu_trcr_mask = 0x0000003f, +        .nwindows = 7, +        .features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_MUL | +        CPU_FEATURE_DIV | CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | +        CPU_FEATURE_FMUL, +    }, +    { +        .name = "TI MicroSparc II", +        .iu_version = 0x42000000, +        .fpu_version = 4 << 17, +        .mmu_version = 0x02000000, +        .mmu_bm = 0x00004000, +        .mmu_ctpr_mask = 0x00ffffc0, +        .mmu_cxr_mask = 0x000000ff, +        .mmu_sfsr_mask = 0x00016fff, +        .mmu_trcr_mask = 0x00ffffff, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI MicroSparc IIep", +        .iu_version = 0x42000000, +        .fpu_version = 4 << 17, +        .mmu_version = 0x04000000, +        .mmu_bm = 0x00004000, +        .mmu_ctpr_mask = 0x00ffffc0, +        .mmu_cxr_mask = 0x000000ff, +        .mmu_sfsr_mask = 0x00016bff, +        .mmu_trcr_mask = 0x00ffffff, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI SuperSparc 40", /* STP1020NPGA */ +        .iu_version = 0x41000000, /* SuperSPARC 2.x */ +        .fpu_version = 0 << 17, +        .mmu_version = 0x00000800, /* SuperSPARC 2.x, no MXCC */ +        .mmu_bm = 0x00002000, +        .mmu_ctpr_mask = 0xffffffc0, +        .mmu_cxr_mask = 0x0000ffff, +        .mmu_sfsr_mask = 0xffffffff, +        .mmu_trcr_mask = 0xffffffff, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI SuperSparc 50", /* STP1020PGA */ +        .iu_version = 0x40000000, /* SuperSPARC 3.x */ +        .fpu_version = 0 << 17, +        .mmu_version = 0x01000800, /* SuperSPARC 3.x, no MXCC */ +        .mmu_bm = 0x00002000, +        .mmu_ctpr_mask = 0xffffffc0, +        .mmu_cxr_mask = 0x0000ffff, +        .mmu_sfsr_mask = 0xffffffff, +        .mmu_trcr_mask = 0xffffffff, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI SuperSparc 51", +        .iu_version = 0x40000000, /* SuperSPARC 3.x */ +        .fpu_version = 0 << 17, +        .mmu_version = 0x01000000, /* SuperSPARC 3.x, MXCC */ +        .mmu_bm = 0x00002000, +        .mmu_ctpr_mask = 0xffffffc0, +        .mmu_cxr_mask = 0x0000ffff, +        .mmu_sfsr_mask = 0xffffffff, +        .mmu_trcr_mask = 0xffffffff, +        .mxcc_version = 0x00000104, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI SuperSparc 60", /* STP1020APGA */ +        .iu_version = 0x40000000, /* SuperSPARC 3.x */ +        .fpu_version = 0 << 17, +        .mmu_version = 0x01000800, /* SuperSPARC 3.x, no MXCC */ +        .mmu_bm = 0x00002000, +        .mmu_ctpr_mask = 0xffffffc0, +        .mmu_cxr_mask = 0x0000ffff, +        .mmu_sfsr_mask = 0xffffffff, +        .mmu_trcr_mask = 0xffffffff, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI SuperSparc 61", +        .iu_version = 0x44000000, /* SuperSPARC 3.x */ +        .fpu_version = 0 << 17, +        .mmu_version = 0x01000000, /* SuperSPARC 3.x, MXCC */ +        .mmu_bm = 0x00002000, +        .mmu_ctpr_mask = 0xffffffc0, +        .mmu_cxr_mask = 0x0000ffff, +        .mmu_sfsr_mask = 0xffffffff, +        .mmu_trcr_mask = 0xffffffff, +        .mxcc_version = 0x00000104, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "TI SuperSparc II", +        .iu_version = 0x40000000, /* SuperSPARC II 1.x */ +        .fpu_version = 0 << 17, +        .mmu_version = 0x08000000, /* SuperSPARC II 1.x, MXCC */ +        .mmu_bm = 0x00002000, +        .mmu_ctpr_mask = 0xffffffc0, +        .mmu_cxr_mask = 0x0000ffff, +        .mmu_sfsr_mask = 0xffffffff, +        .mmu_trcr_mask = 0xffffffff, +        .mxcc_version = 0x00000104, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES, +    }, +    { +        .name = "LEON2", +        .iu_version = 0xf2000000, +        .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ +        .mmu_version = 0xf2000000, +        .mmu_bm = 0x00004000, +        .mmu_ctpr_mask = 0x007ffff0, +        .mmu_cxr_mask = 0x0000003f, +        .mmu_sfsr_mask = 0xffffffff, +        .mmu_trcr_mask = 0xffffffff, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN, +    }, +    { +        .name = "LEON3", +        .iu_version = 0xf3000000, +        .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ +        .mmu_version = 0xf3000000, +        .mmu_bm = 0x00000000, +        .mmu_ctpr_mask = 0xfffffffc, +        .mmu_cxr_mask = 0x000000ff, +        .mmu_sfsr_mask = 0xffffffff, +        .mmu_trcr_mask = 0xffffffff, +        .nwindows = 8, +        .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN | +        CPU_FEATURE_ASR17 | CPU_FEATURE_CACHE_CTRL | CPU_FEATURE_POWERDOWN | +        CPU_FEATURE_CASA, +    }, +#endif +}; + +static const char * const feature_name[] = { +    "float", +    "float128", +    "swap", +    "mul", +    "div", +    "flush", +    "fsqrt", +    "fmul", +    "vis1", +    "vis2", +    "fsmuld", +    "hypv", +    "cmt", +    "gl", +}; + +static void print_features(FILE *f, fprintf_function cpu_fprintf, +                           uint32_t features, const char *prefix) +{ +    unsigned int i; + +    for (i = 0; i < ARRAY_SIZE(feature_name); i++) { +        if (feature_name[i] && (features & (1 << i))) { +            if (prefix) { +                (*cpu_fprintf)(f, "%s", prefix); +            } +            (*cpu_fprintf)(f, "%s ", feature_name[i]); +        } +    } +} + +static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features) +{ +    unsigned int i; + +    for (i = 0; i < ARRAY_SIZE(feature_name); i++) { +        if (feature_name[i] && !strcmp(flagname, feature_name[i])) { +            *features |= 1 << i; +            return; +        } +    } +    error_report("CPU feature %s not found", flagname); +} + +static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *name) +{ +    unsigned int i; +    const sparc_def_t *def = NULL; + +    for (i = 0; i < ARRAY_SIZE(sparc_defs); i++) { +        if (strcasecmp(name, sparc_defs[i].name) == 0) { +            def = &sparc_defs[i]; +        } +    } +    if (!def) { +        return -1; +    } +    memcpy(cpu_def, def, sizeof(*def)); +    return 0; +} + +static void sparc_cpu_parse_features(CPUState *cs, char *features, +                                     Error **errp) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    sparc_def_t *cpu_def = cpu->env.def; +    char *featurestr; +    uint32_t plus_features = 0; +    uint32_t minus_features = 0; +    uint64_t iu_version; +    uint32_t fpu_version, mmu_version, nwindows; + +    featurestr = features ? strtok(features, ",") : NULL; +    while (featurestr) { +        char *val; + +        if (featurestr[0] == '+') { +            add_flagname_to_bitmaps(featurestr + 1, &plus_features); +        } else if (featurestr[0] == '-') { +            add_flagname_to_bitmaps(featurestr + 1, &minus_features); +        } else if ((val = strchr(featurestr, '='))) { +            *val = 0; val++; +            if (!strcmp(featurestr, "iu_version")) { +                char *err; + +                iu_version = strtoll(val, &err, 0); +                if (!*val || *err) { +                    error_setg(errp, "bad numerical value %s", val); +                    return; +                } +                cpu_def->iu_version = iu_version; +#ifdef DEBUG_FEATURES +                fprintf(stderr, "iu_version %" PRIx64 "\n", iu_version); +#endif +            } else if (!strcmp(featurestr, "fpu_version")) { +                char *err; + +                fpu_version = strtol(val, &err, 0); +                if (!*val || *err) { +                    error_setg(errp, "bad numerical value %s", val); +                    return; +                } +                cpu_def->fpu_version = fpu_version; +#ifdef DEBUG_FEATURES +                fprintf(stderr, "fpu_version %x\n", fpu_version); +#endif +            } else if (!strcmp(featurestr, "mmu_version")) { +                char *err; + +                mmu_version = strtol(val, &err, 0); +                if (!*val || *err) { +                    error_setg(errp, "bad numerical value %s", val); +                    return; +                } +                cpu_def->mmu_version = mmu_version; +#ifdef DEBUG_FEATURES +                fprintf(stderr, "mmu_version %x\n", mmu_version); +#endif +            } else if (!strcmp(featurestr, "nwindows")) { +                char *err; + +                nwindows = strtol(val, &err, 0); +                if (!*val || *err || nwindows > MAX_NWINDOWS || +                    nwindows < MIN_NWINDOWS) { +                    error_setg(errp, "bad numerical value %s", val); +                    return; +                } +                cpu_def->nwindows = nwindows; +#ifdef DEBUG_FEATURES +                fprintf(stderr, "nwindows %d\n", nwindows); +#endif +            } else { +                error_setg(errp, "unrecognized feature %s", featurestr); +                return; +            } +        } else { +            error_setg(errp, "feature string `%s' not in format " +                             "(+feature|-feature|feature=xyz)", featurestr); +            return; +        } +        featurestr = strtok(NULL, ","); +    } +    cpu_def->features |= plus_features; +    cpu_def->features &= ~minus_features; +#ifdef DEBUG_FEATURES +    print_features(stderr, fprintf, cpu_def->features, NULL); +#endif +} + +void sparc_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ +    unsigned int i; + +    for (i = 0; i < ARRAY_SIZE(sparc_defs); i++) { +        (*cpu_fprintf)(f, "Sparc %16s IU " TARGET_FMT_lx +                       " FPU %08x MMU %08x NWINS %d ", +                       sparc_defs[i].name, +                       sparc_defs[i].iu_version, +                       sparc_defs[i].fpu_version, +                       sparc_defs[i].mmu_version, +                       sparc_defs[i].nwindows); +        print_features(f, cpu_fprintf, CPU_DEFAULT_FEATURES & +                       ~sparc_defs[i].features, "-"); +        print_features(f, cpu_fprintf, ~CPU_DEFAULT_FEATURES & +                       sparc_defs[i].features, "+"); +        (*cpu_fprintf)(f, "\n"); +    } +    (*cpu_fprintf)(f, "Default CPU feature flags (use '-' to remove): "); +    print_features(f, cpu_fprintf, CPU_DEFAULT_FEATURES, NULL); +    (*cpu_fprintf)(f, "\n"); +    (*cpu_fprintf)(f, "Available CPU feature flags (use '+' to add): "); +    print_features(f, cpu_fprintf, ~CPU_DEFAULT_FEATURES, NULL); +    (*cpu_fprintf)(f, "\n"); +    (*cpu_fprintf)(f, "Numerical features (use '=' to set): iu_version " +                   "fpu_version mmu_version nwindows\n"); +} + +static void cpu_print_cc(FILE *f, fprintf_function cpu_fprintf, +                         uint32_t cc) +{ +    cpu_fprintf(f, "%c%c%c%c", cc & PSR_NEG ? 'N' : '-', +                cc & PSR_ZERO ? 'Z' : '-', cc & PSR_OVF ? 'V' : '-', +                cc & PSR_CARRY ? 'C' : '-'); +} + +#ifdef TARGET_SPARC64 +#define REGS_PER_LINE 4 +#else +#define REGS_PER_LINE 8 +#endif + +void sparc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, +                          int flags) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; +    int i, x; + +    cpu_fprintf(f, "pc: " TARGET_FMT_lx "  npc: " TARGET_FMT_lx "\n", env->pc, +                env->npc); + +    for (i = 0; i < 8; i++) { +        if (i % REGS_PER_LINE == 0) { +            cpu_fprintf(f, "%%g%d-%d:", i, i + REGS_PER_LINE - 1); +        } +        cpu_fprintf(f, " " TARGET_FMT_lx, env->gregs[i]); +        if (i % REGS_PER_LINE == REGS_PER_LINE - 1) { +            cpu_fprintf(f, "\n"); +        } +    } +    for (x = 0; x < 3; x++) { +        for (i = 0; i < 8; i++) { +            if (i % REGS_PER_LINE == 0) { +                cpu_fprintf(f, "%%%c%d-%d: ", +                            x == 0 ? 'o' : (x == 1 ? 'l' : 'i'), +                            i, i + REGS_PER_LINE - 1); +            } +            cpu_fprintf(f, TARGET_FMT_lx " ", env->regwptr[i + x * 8]); +            if (i % REGS_PER_LINE == REGS_PER_LINE - 1) { +                cpu_fprintf(f, "\n"); +            } +        } +    } + +    for (i = 0; i < TARGET_DPREGS; i++) { +        if ((i & 3) == 0) { +            cpu_fprintf(f, "%%f%02d: ", i * 2); +        } +        cpu_fprintf(f, " %016" PRIx64, env->fpr[i].ll); +        if ((i & 3) == 3) { +            cpu_fprintf(f, "\n"); +        } +    } +#ifdef TARGET_SPARC64 +    cpu_fprintf(f, "pstate: %08x ccr: %02x (icc: ", env->pstate, +                (unsigned)cpu_get_ccr(env)); +    cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << PSR_CARRY_SHIFT); +    cpu_fprintf(f, " xcc: "); +    cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << (PSR_CARRY_SHIFT - 4)); +    cpu_fprintf(f, ") asi: %02x tl: %d pil: %x\n", env->asi, env->tl, +                env->psrpil); +    cpu_fprintf(f, "cansave: %d canrestore: %d otherwin: %d wstate: %d " +                "cleanwin: %d cwp: %d\n", +                env->cansave, env->canrestore, env->otherwin, env->wstate, +                env->cleanwin, env->nwindows - 1 - env->cwp); +    cpu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx " fprs: " +                TARGET_FMT_lx "\n", env->fsr, env->y, env->fprs); +#else +    cpu_fprintf(f, "psr: %08x (icc: ", cpu_get_psr(env)); +    cpu_print_cc(f, cpu_fprintf, cpu_get_psr(env)); +    cpu_fprintf(f, " SPE: %c%c%c) wim: %08x\n", env->psrs ? 'S' : '-', +                env->psrps ? 'P' : '-', env->psret ? 'E' : '-', +                env->wim); +    cpu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx "\n", +                env->fsr, env->y); +#endif +    cpu_fprintf(f, "\n"); +} + +static void sparc_cpu_set_pc(CPUState *cs, vaddr value) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); + +    cpu->env.pc = value; +    cpu->env.npc = value + 4; +} + +static void sparc_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); + +    cpu->env.pc = tb->pc; +    cpu->env.npc = tb->cs_base; +} + +static bool sparc_cpu_has_work(CPUState *cs) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; + +    return (cs->interrupt_request & CPU_INTERRUPT_HARD) && +           cpu_interrupts_enabled(env); +} + +static void sparc_cpu_realizefn(DeviceState *dev, Error **errp) +{ +    SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(dev); +#if defined(CONFIG_USER_ONLY) +    SPARCCPU *cpu = SPARC_CPU(dev); +    CPUSPARCState *env = &cpu->env; + +    if ((env->def->features & CPU_FEATURE_FLOAT)) { +        env->def->features |= CPU_FEATURE_FLOAT128; +    } +#endif + +    qemu_init_vcpu(CPU(dev)); + +    scc->parent_realize(dev, errp); +} + +static void sparc_cpu_initfn(Object *obj) +{ +    CPUState *cs = CPU(obj); +    SPARCCPU *cpu = SPARC_CPU(obj); +    CPUSPARCState *env = &cpu->env; + +    cs->env_ptr = env; +    cpu_exec_init(cs, &error_abort); + +    if (tcg_enabled()) { +        gen_intermediate_code_init(env); +    } +} + +static void sparc_cpu_uninitfn(Object *obj) +{ +    SPARCCPU *cpu = SPARC_CPU(obj); +    CPUSPARCState *env = &cpu->env; + +    g_free(env->def); +} + +static void sparc_cpu_class_init(ObjectClass *oc, void *data) +{ +    SPARCCPUClass *scc = SPARC_CPU_CLASS(oc); +    CPUClass *cc = CPU_CLASS(oc); +    DeviceClass *dc = DEVICE_CLASS(oc); + +    scc->parent_realize = dc->realize; +    dc->realize = sparc_cpu_realizefn; + +    scc->parent_reset = cc->reset; +    cc->reset = sparc_cpu_reset; + +    cc->parse_features = sparc_cpu_parse_features; +    cc->has_work = sparc_cpu_has_work; +    cc->do_interrupt = sparc_cpu_do_interrupt; +    cc->cpu_exec_interrupt = sparc_cpu_exec_interrupt; +    cc->dump_state = sparc_cpu_dump_state; +#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) +    cc->memory_rw_debug = sparc_cpu_memory_rw_debug; +#endif +    cc->set_pc = sparc_cpu_set_pc; +    cc->synchronize_from_tb = sparc_cpu_synchronize_from_tb; +    cc->gdb_read_register = sparc_cpu_gdb_read_register; +    cc->gdb_write_register = sparc_cpu_gdb_write_register; +#ifdef CONFIG_USER_ONLY +    cc->handle_mmu_fault = sparc_cpu_handle_mmu_fault; +#else +    cc->do_unassigned_access = sparc_cpu_unassigned_access; +    cc->do_unaligned_access = sparc_cpu_do_unaligned_access; +    cc->get_phys_page_debug = sparc_cpu_get_phys_page_debug; +#endif + +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) +    cc->gdb_num_core_regs = 86; +#else +    cc->gdb_num_core_regs = 72; +#endif + +    /* +     * Reason: sparc_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 sparc_cpu_type_info = { +    .name = TYPE_SPARC_CPU, +    .parent = TYPE_CPU, +    .instance_size = sizeof(SPARCCPU), +    .instance_init = sparc_cpu_initfn, +    .instance_finalize = sparc_cpu_uninitfn, +    .abstract = false, +    .class_size = sizeof(SPARCCPUClass), +    .class_init = sparc_cpu_class_init, +}; + +static void sparc_cpu_register_types(void) +{ +    type_register_static(&sparc_cpu_type_info); +} + +type_init(sparc_cpu_register_types) diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h new file mode 100644 index 00000000..0522b65f --- /dev/null +++ b/target-sparc/cpu.h @@ -0,0 +1,747 @@ +#ifndef CPU_SPARC_H +#define CPU_SPARC_H + +#include "config.h" +#include "qemu-common.h" +#include "qemu/bswap.h" + +#define ALIGNED_ONLY + +#if !defined(TARGET_SPARC64) +#define TARGET_LONG_BITS 32 +#define TARGET_DPREGS 16 +#define TARGET_PAGE_BITS 12 /* 4k */ +#define TARGET_PHYS_ADDR_SPACE_BITS 36 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#else +#define TARGET_LONG_BITS 64 +#define TARGET_DPREGS 32 +#define TARGET_PAGE_BITS 13 /* 8k */ +#define TARGET_PHYS_ADDR_SPACE_BITS 41 +# ifdef TARGET_ABI32 +#  define TARGET_VIRT_ADDR_SPACE_BITS 32 +# else +#  define TARGET_VIRT_ADDR_SPACE_BITS 44 +# endif +#endif + +#define CPUArchState struct CPUSPARCState + +#include "exec/cpu-defs.h" + +#include "fpu/softfloat.h" + +#if !defined(TARGET_SPARC64) +#define ELF_MACHINE     EM_SPARC +#else +#define ELF_MACHINE     EM_SPARCV9 +#endif + +/*#define EXCP_INTERRUPT 0x100*/ + +/* trap definitions */ +#ifndef TARGET_SPARC64 +#define TT_TFAULT   0x01 +#define TT_ILL_INSN 0x02 +#define TT_PRIV_INSN 0x03 +#define TT_NFPU_INSN 0x04 +#define TT_WIN_OVF  0x05 +#define TT_WIN_UNF  0x06 +#define TT_UNALIGNED 0x07 +#define TT_FP_EXCP  0x08 +#define TT_DFAULT   0x09 +#define TT_TOVF     0x0a +#define TT_EXTINT   0x10 +#define TT_CODE_ACCESS 0x21 +#define TT_UNIMP_FLUSH 0x25 +#define TT_DATA_ACCESS 0x29 +#define TT_DIV_ZERO 0x2a +#define TT_NCP_INSN 0x24 +#define TT_TRAP     0x80 +#else +#define TT_POWER_ON_RESET 0x01 +#define TT_TFAULT   0x08 +#define TT_CODE_ACCESS 0x0a +#define TT_ILL_INSN 0x10 +#define TT_UNIMP_FLUSH TT_ILL_INSN +#define TT_PRIV_INSN 0x11 +#define TT_NFPU_INSN 0x20 +#define TT_FP_EXCP  0x21 +#define TT_TOVF     0x23 +#define TT_CLRWIN   0x24 +#define TT_DIV_ZERO 0x28 +#define TT_DFAULT   0x30 +#define TT_DATA_ACCESS 0x32 +#define TT_UNALIGNED 0x34 +#define TT_PRIV_ACT 0x37 +#define TT_EXTINT   0x40 +#define TT_IVEC     0x60 +#define TT_TMISS    0x64 +#define TT_DMISS    0x68 +#define TT_DPROT    0x6c +#define TT_SPILL    0x80 +#define TT_FILL     0xc0 +#define TT_WOTHER   (1 << 5) +#define TT_TRAP     0x100 +#endif + +#define PSR_NEG_SHIFT 23 +#define PSR_NEG   (1 << PSR_NEG_SHIFT) +#define PSR_ZERO_SHIFT 22 +#define PSR_ZERO  (1 << PSR_ZERO_SHIFT) +#define PSR_OVF_SHIFT 21 +#define PSR_OVF   (1 << PSR_OVF_SHIFT) +#define PSR_CARRY_SHIFT 20 +#define PSR_CARRY (1 << PSR_CARRY_SHIFT) +#define PSR_ICC   (PSR_NEG|PSR_ZERO|PSR_OVF|PSR_CARRY) +#if !defined(TARGET_SPARC64) +#define PSR_EF    (1<<12) +#define PSR_PIL   0xf00 +#define PSR_S     (1<<7) +#define PSR_PS    (1<<6) +#define PSR_ET    (1<<5) +#define PSR_CWP   0x1f +#endif + +#define CC_SRC (env->cc_src) +#define CC_SRC2 (env->cc_src2) +#define CC_DST (env->cc_dst) +#define CC_OP  (env->cc_op) + +enum { +    CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */ +    CC_OP_FLAGS,   /* all cc are back in status register */ +    CC_OP_DIV,     /* modify N, Z and V, C = 0*/ +    CC_OP_ADD,     /* modify all flags, CC_DST = res, CC_SRC = src1 */ +    CC_OP_ADDX,    /* modify all flags, CC_DST = res, CC_SRC = src1 */ +    CC_OP_TADD,    /* modify all flags, CC_DST = res, CC_SRC = src1 */ +    CC_OP_TADDTV,  /* modify all flags except V, CC_DST = res, CC_SRC = src1 */ +    CC_OP_SUB,     /* modify all flags, CC_DST = res, CC_SRC = src1 */ +    CC_OP_SUBX,    /* modify all flags, CC_DST = res, CC_SRC = src1 */ +    CC_OP_TSUB,    /* modify all flags, CC_DST = res, CC_SRC = src1 */ +    CC_OP_TSUBTV,  /* modify all flags except V, CC_DST = res, CC_SRC = src1 */ +    CC_OP_LOGIC,   /* modify N and Z, C = V = 0, CC_DST = res */ +    CC_OP_NB, +}; + +/* Trap base register */ +#define TBR_BASE_MASK 0xfffff000 + +#if defined(TARGET_SPARC64) +#define PS_TCT   (1<<12) /* UA2007, impl.dep. trap on control transfer */ +#define PS_IG    (1<<11) /* v9, zero on UA2007 */ +#define PS_MG    (1<<10) /* v9, zero on UA2007 */ +#define PS_CLE   (1<<9) /* UA2007 */ +#define PS_TLE   (1<<8) /* UA2007 */ +#define PS_RMO   (1<<7) +#define PS_RED   (1<<5) /* v9, zero on UA2007 */ +#define PS_PEF   (1<<4) /* enable fpu */ +#define PS_AM    (1<<3) /* address mask */ +#define PS_PRIV  (1<<2) +#define PS_IE    (1<<1) +#define PS_AG    (1<<0) /* v9, zero on UA2007 */ + +#define FPRS_FEF (1<<2) + +#define HS_PRIV  (1<<2) +#endif + +/* Fcc */ +#define FSR_RD1        (1ULL << 31) +#define FSR_RD0        (1ULL << 30) +#define FSR_RD_MASK    (FSR_RD1 | FSR_RD0) +#define FSR_RD_NEAREST 0 +#define FSR_RD_ZERO    FSR_RD0 +#define FSR_RD_POS     FSR_RD1 +#define FSR_RD_NEG     (FSR_RD1 | FSR_RD0) + +#define FSR_NVM   (1ULL << 27) +#define FSR_OFM   (1ULL << 26) +#define FSR_UFM   (1ULL << 25) +#define FSR_DZM   (1ULL << 24) +#define FSR_NXM   (1ULL << 23) +#define FSR_TEM_MASK (FSR_NVM | FSR_OFM | FSR_UFM | FSR_DZM | FSR_NXM) + +#define FSR_NVA   (1ULL << 9) +#define FSR_OFA   (1ULL << 8) +#define FSR_UFA   (1ULL << 7) +#define FSR_DZA   (1ULL << 6) +#define FSR_NXA   (1ULL << 5) +#define FSR_AEXC_MASK (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) + +#define FSR_NVC   (1ULL << 4) +#define FSR_OFC   (1ULL << 3) +#define FSR_UFC   (1ULL << 2) +#define FSR_DZC   (1ULL << 1) +#define FSR_NXC   (1ULL << 0) +#define FSR_CEXC_MASK (FSR_NVC | FSR_OFC | FSR_UFC | FSR_DZC | FSR_NXC) + +#define FSR_FTT2   (1ULL << 16) +#define FSR_FTT1   (1ULL << 15) +#define FSR_FTT0   (1ULL << 14) +//gcc warns about constant overflow for ~FSR_FTT_MASK +//#define FSR_FTT_MASK (FSR_FTT2 | FSR_FTT1 | FSR_FTT0) +#ifdef TARGET_SPARC64 +#define FSR_FTT_NMASK      0xfffffffffffe3fffULL +#define FSR_FTT_CEXC_NMASK 0xfffffffffffe3fe0ULL +#define FSR_LDFSR_OLDMASK  0x0000003f000fc000ULL +#define FSR_LDXFSR_MASK    0x0000003fcfc00fffULL +#define FSR_LDXFSR_OLDMASK 0x00000000000fc000ULL +#else +#define FSR_FTT_NMASK      0xfffe3fffULL +#define FSR_FTT_CEXC_NMASK 0xfffe3fe0ULL +#define FSR_LDFSR_OLDMASK  0x000fc000ULL +#endif +#define FSR_LDFSR_MASK     0xcfc00fffULL +#define FSR_FTT_IEEE_EXCP (1ULL << 14) +#define FSR_FTT_UNIMPFPOP (3ULL << 14) +#define FSR_FTT_SEQ_ERROR (4ULL << 14) +#define FSR_FTT_INVAL_FPR (6ULL << 14) + +#define FSR_FCC1_SHIFT 11 +#define FSR_FCC1  (1ULL << FSR_FCC1_SHIFT) +#define FSR_FCC0_SHIFT 10 +#define FSR_FCC0  (1ULL << FSR_FCC0_SHIFT) + +/* MMU */ +#define MMU_E     (1<<0) +#define MMU_NF    (1<<1) + +#define PTE_ENTRYTYPE_MASK 3 +#define PTE_ACCESS_MASK    0x1c +#define PTE_ACCESS_SHIFT   2 +#define PTE_PPN_SHIFT      7 +#define PTE_ADDR_MASK      0xffffff00 + +#define PG_ACCESSED_BIT 5 +#define PG_MODIFIED_BIT 6 +#define PG_CACHE_BIT    7 + +#define PG_ACCESSED_MASK (1 << PG_ACCESSED_BIT) +#define PG_MODIFIED_MASK (1 << PG_MODIFIED_BIT) +#define PG_CACHE_MASK    (1 << PG_CACHE_BIT) + +/* 3 <= NWINDOWS <= 32. */ +#define MIN_NWINDOWS 3 +#define MAX_NWINDOWS 32 + +#if !defined(TARGET_SPARC64) +#define NB_MMU_MODES 2 +#else +#define NB_MMU_MODES 6 +typedef struct trap_state { +    uint64_t tpc; +    uint64_t tnpc; +    uint64_t tstate; +    uint32_t tt; +} trap_state; +#endif + +typedef struct sparc_def_t { +    const char *name; +    target_ulong iu_version; +    uint32_t fpu_version; +    uint32_t mmu_version; +    uint32_t mmu_bm; +    uint32_t mmu_ctpr_mask; +    uint32_t mmu_cxr_mask; +    uint32_t mmu_sfsr_mask; +    uint32_t mmu_trcr_mask; +    uint32_t mxcc_version; +    uint32_t features; +    uint32_t nwindows; +    uint32_t maxtl; +} sparc_def_t; + +#define CPU_FEATURE_FLOAT        (1 << 0) +#define CPU_FEATURE_FLOAT128     (1 << 1) +#define CPU_FEATURE_SWAP         (1 << 2) +#define CPU_FEATURE_MUL          (1 << 3) +#define CPU_FEATURE_DIV          (1 << 4) +#define CPU_FEATURE_FLUSH        (1 << 5) +#define CPU_FEATURE_FSQRT        (1 << 6) +#define CPU_FEATURE_FMUL         (1 << 7) +#define CPU_FEATURE_VIS1         (1 << 8) +#define CPU_FEATURE_VIS2         (1 << 9) +#define CPU_FEATURE_FSMULD       (1 << 10) +#define CPU_FEATURE_HYPV         (1 << 11) +#define CPU_FEATURE_CMT          (1 << 12) +#define CPU_FEATURE_GL           (1 << 13) +#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */ +#define CPU_FEATURE_ASR17        (1 << 15) +#define CPU_FEATURE_CACHE_CTRL   (1 << 16) +#define CPU_FEATURE_POWERDOWN    (1 << 17) +#define CPU_FEATURE_CASA         (1 << 18) + +#ifndef TARGET_SPARC64 +#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP |  \ +                              CPU_FEATURE_MUL | CPU_FEATURE_DIV |     \ +                              CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | \ +                              CPU_FEATURE_FMUL | CPU_FEATURE_FSMULD) +#else +#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP |  \ +                              CPU_FEATURE_MUL | CPU_FEATURE_DIV |     \ +                              CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | \ +                              CPU_FEATURE_FMUL | CPU_FEATURE_VIS1 |   \ +                              CPU_FEATURE_VIS2 | CPU_FEATURE_FSMULD | \ +                              CPU_FEATURE_CASA) +enum { +    mmu_us_12, // Ultrasparc < III (64 entry TLB) +    mmu_us_3,  // Ultrasparc III (512 entry TLB) +    mmu_us_4,  // Ultrasparc IV (several TLBs, 32 and 256MB pages) +    mmu_sun4v, // T1, T2 +}; +#endif + +#define TTE_VALID_BIT       (1ULL << 63) +#define TTE_NFO_BIT         (1ULL << 60) +#define TTE_USED_BIT        (1ULL << 41) +#define TTE_LOCKED_BIT      (1ULL <<  6) +#define TTE_SIDEEFFECT_BIT  (1ULL <<  3) +#define TTE_PRIV_BIT        (1ULL <<  2) +#define TTE_W_OK_BIT        (1ULL <<  1) +#define TTE_GLOBAL_BIT      (1ULL <<  0) + +#define TTE_IS_VALID(tte)   ((tte) & TTE_VALID_BIT) +#define TTE_IS_NFO(tte)     ((tte) & TTE_NFO_BIT) +#define TTE_IS_USED(tte)    ((tte) & TTE_USED_BIT) +#define TTE_IS_LOCKED(tte)  ((tte) & TTE_LOCKED_BIT) +#define TTE_IS_SIDEEFFECT(tte) ((tte) & TTE_SIDEEFFECT_BIT) +#define TTE_IS_PRIV(tte)    ((tte) & TTE_PRIV_BIT) +#define TTE_IS_W_OK(tte)    ((tte) & TTE_W_OK_BIT) +#define TTE_IS_GLOBAL(tte)  ((tte) & TTE_GLOBAL_BIT) + +#define TTE_SET_USED(tte)   ((tte) |= TTE_USED_BIT) +#define TTE_SET_UNUSED(tte) ((tte) &= ~TTE_USED_BIT) + +#define TTE_PGSIZE(tte)     (((tte) >> 61) & 3ULL) +#define TTE_PA(tte)         ((tte) & 0x1ffffffe000ULL) + +#define SFSR_NF_BIT         (1ULL << 24)   /* JPS1 NoFault */ +#define SFSR_TM_BIT         (1ULL << 15)   /* JPS1 TLB Miss */ +#define SFSR_FT_VA_IMMU_BIT (1ULL << 13)   /* USIIi VA out of range (IMMU) */ +#define SFSR_FT_VA_DMMU_BIT (1ULL << 12)   /* USIIi VA out of range (DMMU) */ +#define SFSR_FT_NFO_BIT     (1ULL << 11)   /* NFO page access */ +#define SFSR_FT_ILL_BIT     (1ULL << 10)   /* illegal LDA/STA ASI */ +#define SFSR_FT_ATOMIC_BIT  (1ULL <<  9)   /* atomic op on noncacheable area */ +#define SFSR_FT_NF_E_BIT    (1ULL <<  8)   /* NF access on side effect area */ +#define SFSR_FT_PRIV_BIT    (1ULL <<  7)   /* privilege violation */ +#define SFSR_PR_BIT         (1ULL <<  3)   /* privilege mode */ +#define SFSR_WRITE_BIT      (1ULL <<  2)   /* write access mode */ +#define SFSR_OW_BIT         (1ULL <<  1)   /* status overwritten */ +#define SFSR_VALID_BIT      (1ULL <<  0)   /* status valid */ + +#define SFSR_ASI_SHIFT      16             /* 23:16 ASI value */ +#define SFSR_ASI_MASK       (0xffULL << SFSR_ASI_SHIFT) +#define SFSR_CT_PRIMARY     (0ULL <<  4)   /* 5:4 context type */ +#define SFSR_CT_SECONDARY   (1ULL <<  4) +#define SFSR_CT_NUCLEUS     (2ULL <<  4) +#define SFSR_CT_NOTRANS     (3ULL <<  4) +#define SFSR_CT_MASK        (3ULL <<  4) + +/* Leon3 cache control */ + +/* Cache control: emulate the behavior of cache control registers but without +   any effect on the emulated */ + +#define CACHE_STATE_MASK 0x3 +#define CACHE_DISABLED   0x0 +#define CACHE_FROZEN     0x1 +#define CACHE_ENABLED    0x3 + +/* Cache Control register fields */ + +#define CACHE_CTRL_IF (1 <<  4)  /* Instruction Cache Freeze on Interrupt */ +#define CACHE_CTRL_DF (1 <<  5)  /* Data Cache Freeze on Interrupt */ +#define CACHE_CTRL_DP (1 << 14)  /* Data cache flush pending */ +#define CACHE_CTRL_IP (1 << 15)  /* Instruction cache flush pending */ +#define CACHE_CTRL_IB (1 << 16)  /* Instruction burst fetch */ +#define CACHE_CTRL_FI (1 << 21)  /* Flush Instruction cache (Write only) */ +#define CACHE_CTRL_FD (1 << 22)  /* Flush Data cache (Write only) */ +#define CACHE_CTRL_DS (1 << 23)  /* Data cache snoop enable */ + +typedef struct SparcTLBEntry { +    uint64_t tag; +    uint64_t tte; +} SparcTLBEntry; + +struct CPUTimer +{ +    const char *name; +    uint32_t    frequency; +    uint32_t    disabled; +    uint64_t    disabled_mask; +    int64_t     clock_offset; +    QEMUTimer  *qtimer; +}; + +typedef struct CPUTimer CPUTimer; + +struct QEMUFile; +void cpu_put_timer(struct QEMUFile *f, CPUTimer *s); +void cpu_get_timer(struct QEMUFile *f, CPUTimer *s); + +typedef struct CPUSPARCState CPUSPARCState; + +struct CPUSPARCState { +    target_ulong gregs[8]; /* general registers */ +    target_ulong *regwptr; /* pointer to current register window */ +    target_ulong pc;       /* program counter */ +    target_ulong npc;      /* next program counter */ +    target_ulong y;        /* multiply/divide register */ + +    /* emulator internal flags handling */ +    target_ulong cc_src, cc_src2; +    target_ulong cc_dst; +    uint32_t cc_op; + +    target_ulong cond; /* conditional branch result (XXX: save it in a +                          temporary register when possible) */ + +    uint32_t psr;      /* processor state register */ +    target_ulong fsr;      /* FPU state register */ +    CPU_DoubleU fpr[TARGET_DPREGS];  /* floating point registers */ +    uint32_t cwp;      /* index of current register window (extracted +                          from PSR) */ +#if !defined(TARGET_SPARC64) || defined(TARGET_ABI32) +    uint32_t wim;      /* window invalid mask */ +#endif +    target_ulong tbr;  /* trap base register */ +#if !defined(TARGET_SPARC64) +    int      psrs;     /* supervisor mode (extracted from PSR) */ +    int      psrps;    /* previous supervisor mode */ +    int      psret;    /* enable traps */ +#endif +    uint32_t psrpil;   /* interrupt blocking level */ +    uint32_t pil_in;   /* incoming interrupt level bitmap */ +#if !defined(TARGET_SPARC64) +    int      psref;    /* enable fpu */ +#endif +    int interrupt_index; +    /* NOTE: we allow 8 more registers to handle wrapping */ +    target_ulong regbase[MAX_NWINDOWS * 16 + 8]; + +    CPU_COMMON + +    /* Fields from here on are preserved across CPU reset. */ +    target_ulong version; +    uint32_t nwindows; + +    /* MMU regs */ +#if defined(TARGET_SPARC64) +    uint64_t lsu; +#define DMMU_E 0x8 +#define IMMU_E 0x4 +    //typedef struct SparcMMU +    union { +        uint64_t immuregs[16]; +        struct { +            uint64_t tsb_tag_target; +            uint64_t unused_mmu_primary_context;   // use DMMU +            uint64_t unused_mmu_secondary_context; // use DMMU +            uint64_t sfsr; +            uint64_t sfar; +            uint64_t tsb; +            uint64_t tag_access; +        } immu; +    }; +    union { +        uint64_t dmmuregs[16]; +        struct { +            uint64_t tsb_tag_target; +            uint64_t mmu_primary_context; +            uint64_t mmu_secondary_context; +            uint64_t sfsr; +            uint64_t sfar; +            uint64_t tsb; +            uint64_t tag_access; +        } dmmu; +    }; +    SparcTLBEntry itlb[64]; +    SparcTLBEntry dtlb[64]; +    uint32_t mmu_version; +#else +    uint32_t mmuregs[32]; +    uint64_t mxccdata[4]; +    uint64_t mxccregs[8]; +    uint32_t mmubpctrv, mmubpctrc, mmubpctrs; +    uint64_t mmubpaction; +    uint64_t mmubpregs[4]; +    uint64_t prom_addr; +#endif +    /* temporary float registers */ +    float128 qt0, qt1; +    float_status fp_status; +#if defined(TARGET_SPARC64) +#define MAXTL_MAX 8 +#define MAXTL_MASK (MAXTL_MAX - 1) +    trap_state ts[MAXTL_MAX]; +    uint32_t xcc;               /* Extended integer condition codes */ +    uint32_t asi; +    uint32_t pstate; +    uint32_t tl; +    uint32_t maxtl; +    uint32_t cansave, canrestore, otherwin, wstate, cleanwin; +    uint64_t agregs[8]; /* alternate general registers */ +    uint64_t bgregs[8]; /* backup for normal global registers */ +    uint64_t igregs[8]; /* interrupt general registers */ +    uint64_t mgregs[8]; /* mmu general registers */ +    uint64_t fprs; +    uint64_t tick_cmpr, stick_cmpr; +    CPUTimer *tick, *stick; +#define TICK_NPT_MASK        0x8000000000000000ULL +#define TICK_INT_DIS         0x8000000000000000ULL +    uint64_t gsr; +    uint32_t gl; // UA2005 +    /* UA 2005 hyperprivileged registers */ +    uint64_t hpstate, htstate[MAXTL_MAX], hintp, htba, hver, hstick_cmpr, ssr; +    CPUTimer *hstick; // UA 2005 +    /* Interrupt vector registers */ +    uint64_t ivec_status; +    uint64_t ivec_data[3]; +    uint32_t softint; +#define SOFTINT_TIMER   1 +#define SOFTINT_STIMER  (1 << 16) +#define SOFTINT_INTRMASK (0xFFFE) +#define SOFTINT_REG_MASK (SOFTINT_STIMER|SOFTINT_INTRMASK|SOFTINT_TIMER) +#endif +    sparc_def_t *def; + +    void *irq_manager; +    void (*qemu_irq_ack)(CPUSPARCState *env, void *irq_manager, int intno); + +    /* Leon3 cache control */ +    uint32_t cache_control; +}; + +#include "cpu-qom.h" + +#ifndef NO_CPU_IO_DEFS +/* cpu_init.c */ +SPARCCPU *cpu_sparc_init(const char *cpu_model); +void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu); +void sparc_cpu_list(FILE *f, fprintf_function cpu_fprintf); +/* mmu_helper.c */ +int sparc_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, +                               int mmu_idx); +target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev); +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUSPARCState *env); + +#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) +int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr, +                              uint8_t *buf, int len, bool is_write); +#endif + + +/* translate.c */ +void gen_intermediate_code_init(CPUSPARCState *env); + +/* cpu-exec.c */ +int cpu_sparc_exec(CPUState *cpu); + +/* win_helper.c */ +target_ulong cpu_get_psr(CPUSPARCState *env1); +void cpu_put_psr(CPUSPARCState *env1, target_ulong val); +#ifdef TARGET_SPARC64 +target_ulong cpu_get_ccr(CPUSPARCState *env1); +void cpu_put_ccr(CPUSPARCState *env1, target_ulong val); +target_ulong cpu_get_cwp64(CPUSPARCState *env1); +void cpu_put_cwp64(CPUSPARCState *env1, int cwp); +void cpu_change_pstate(CPUSPARCState *env1, uint32_t new_pstate); +#endif +int cpu_cwp_inc(CPUSPARCState *env1, int cwp); +int cpu_cwp_dec(CPUSPARCState *env1, int cwp); +void cpu_set_cwp(CPUSPARCState *env1, int new_cwp); + +/* int_helper.c */ +void leon3_irq_manager(CPUSPARCState *env, void *irq_manager, int intno); + +/* sun4m.c, sun4u.c */ +void cpu_check_irqs(CPUSPARCState *env); + +/* leon3.c */ +void leon3_irq_ack(void *irq_manager, int intno); + +#if defined (TARGET_SPARC64) + +static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask) +{ +    return (x & mask) == (y & mask); +} + +#define MMU_CONTEXT_BITS 13 +#define MMU_CONTEXT_MASK ((1 << MMU_CONTEXT_BITS) - 1) + +static inline int tlb_compare_context(const SparcTLBEntry *tlb, +                                      uint64_t context) +{ +    return compare_masked(context, tlb->tag, MMU_CONTEXT_MASK); +} + +#endif +#endif + +/* cpu-exec.c */ +#if !defined(CONFIG_USER_ONLY) +void sparc_cpu_unassigned_access(CPUState *cpu, hwaddr addr, +                                 bool is_write, bool is_exec, int is_asi, +                                 unsigned size); +#if defined(TARGET_SPARC64) +hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr, +                                           int mmu_idx); +#endif +#endif +int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc); + +#ifndef NO_CPU_IO_DEFS +#define cpu_init(cpu_model) CPU(cpu_sparc_init(cpu_model)) +#endif + +#define cpu_exec cpu_sparc_exec +#define cpu_gen_code cpu_sparc_gen_code +#define cpu_signal_handler cpu_sparc_signal_handler +#define cpu_list sparc_cpu_list + +#define CPU_SAVE_VERSION 7 + +/* MMU modes definitions */ +#if defined (TARGET_SPARC64) +#define MMU_USER_IDX   0 +#define MMU_MODE0_SUFFIX _user +#define MMU_USER_SECONDARY_IDX   1 +#define MMU_MODE1_SUFFIX _user_secondary +#define MMU_KERNEL_IDX 2 +#define MMU_MODE2_SUFFIX _kernel +#define MMU_KERNEL_SECONDARY_IDX 3 +#define MMU_MODE3_SUFFIX _kernel_secondary +#define MMU_NUCLEUS_IDX 4 +#define MMU_MODE4_SUFFIX _nucleus +#define MMU_HYPV_IDX   5 +#define MMU_MODE5_SUFFIX _hypv +#else +#define MMU_USER_IDX   0 +#define MMU_MODE0_SUFFIX _user +#define MMU_KERNEL_IDX 1 +#define MMU_MODE1_SUFFIX _kernel +#endif + +#if defined (TARGET_SPARC64) +static inline int cpu_has_hypervisor(CPUSPARCState *env1) +{ +    return env1->def->features & CPU_FEATURE_HYPV; +} + +static inline int cpu_hypervisor_mode(CPUSPARCState *env1) +{ +    return cpu_has_hypervisor(env1) && (env1->hpstate & HS_PRIV); +} + +static inline int cpu_supervisor_mode(CPUSPARCState *env1) +{ +    return env1->pstate & PS_PRIV; +} +#endif + +static inline int cpu_mmu_index(CPUSPARCState *env1) +{ +#if defined(CONFIG_USER_ONLY) +    return MMU_USER_IDX; +#elif !defined(TARGET_SPARC64) +    return env1->psrs; +#else +    if (env1->tl > 0) { +        return MMU_NUCLEUS_IDX; +    } else if (cpu_hypervisor_mode(env1)) { +        return MMU_HYPV_IDX; +    } else if (cpu_supervisor_mode(env1)) { +        return MMU_KERNEL_IDX; +    } else { +        return MMU_USER_IDX; +    } +#endif +} + +static inline int cpu_interrupts_enabled(CPUSPARCState *env1) +{ +#if !defined (TARGET_SPARC64) +    if (env1->psret != 0) +        return 1; +#else +    if (env1->pstate & PS_IE) +        return 1; +#endif + +    return 0; +} + +static inline int cpu_pil_allowed(CPUSPARCState *env1, int pil) +{ +#if !defined(TARGET_SPARC64) +    /* level 15 is non-maskable on sparc v8 */ +    return pil == 15 || pil > env1->psrpil; +#else +    return pil > env1->psrpil; +#endif +} + +#include "exec/cpu-all.h" + +#ifdef TARGET_SPARC64 +/* sun4u.c */ +void cpu_tick_set_count(CPUTimer *timer, uint64_t count); +uint64_t cpu_tick_get_count(CPUTimer *timer); +void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit); +trap_state* cpu_tsptr(CPUSPARCState* env); +#endif + +#define TB_FLAG_FPU_ENABLED (1 << 4) +#define TB_FLAG_AM_ENABLED (1 << 5) + +static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, target_ulong *pc, +                                        target_ulong *cs_base, int *flags) +{ +    *pc = env->pc; +    *cs_base = env->npc; +#ifdef TARGET_SPARC64 +    // AM . Combined FPU enable bits . PRIV . DMMU enabled . IMMU enabled +    *flags = (env->pstate & PS_PRIV)               /* 2 */ +        | ((env->lsu & (DMMU_E | IMMU_E)) >> 2)    /* 1, 0 */ +        | ((env->tl & 0xff) << 8) +        | (env->dmmu.mmu_primary_context << 16);   /* 16... */ +    if (env->pstate & PS_AM) { +        *flags |= TB_FLAG_AM_ENABLED; +    } +    if ((env->def->features & CPU_FEATURE_FLOAT) && (env->pstate & PS_PEF) +        && (env->fprs & FPRS_FEF)) { +        *flags |= TB_FLAG_FPU_ENABLED; +    } +#else +    // FPU enable . Supervisor +    *flags = env->psrs; +    if ((env->def->features & CPU_FEATURE_FLOAT) && env->psref) { +        *flags |= TB_FLAG_FPU_ENABLED; +    } +#endif +} + +static inline bool tb_fpu_enabled(int tb_flags) +{ +#if defined(CONFIG_USER_ONLY) +    return true; +#else +    return tb_flags & TB_FLAG_FPU_ENABLED; +#endif +} + +static inline bool tb_am_enabled(int tb_flags) +{ +#ifndef TARGET_SPARC64 +    return false; +#else +    return tb_flags & TB_FLAG_AM_ENABLED; +#endif +} + +#include "exec/exec-all.h" + +#endif diff --git a/target-sparc/fop_helper.c b/target-sparc/fop_helper.c new file mode 100644 index 00000000..ee4592ef --- /dev/null +++ b/target-sparc/fop_helper.c @@ -0,0 +1,467 @@ +/* + * FPU op helpers + * + *  Copyright (c) 2003-2005 Fabrice Bellard + * + * 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" + +#define QT0 (env->qt0) +#define QT1 (env->qt1) + +static void check_ieee_exceptions(CPUSPARCState *env) +{ +    target_ulong status; + +    status = get_float_exception_flags(&env->fp_status); +    if (status) { +        /* Copy IEEE 754 flags into FSR */ +        if (status & float_flag_invalid) { +            env->fsr |= FSR_NVC; +        } +        if (status & float_flag_overflow) { +            env->fsr |= FSR_OFC; +        } +        if (status & float_flag_underflow) { +            env->fsr |= FSR_UFC; +        } +        if (status & float_flag_divbyzero) { +            env->fsr |= FSR_DZC; +        } +        if (status & float_flag_inexact) { +            env->fsr |= FSR_NXC; +        } + +        if ((env->fsr & FSR_CEXC_MASK) & ((env->fsr & FSR_TEM_MASK) >> 23)) { +            /* Unmasked exception, generate a trap */ +            env->fsr |= FSR_FTT_IEEE_EXCP; +            helper_raise_exception(env, TT_FP_EXCP); +        } else { +            /* Accumulate exceptions */ +            env->fsr |= (env->fsr & FSR_CEXC_MASK) << 5; +        } +    } +} + +static inline void clear_float_exceptions(CPUSPARCState *env) +{ +    set_float_exception_flags(0, &env->fp_status); +} + +#define F_HELPER(name, p) void helper_f##name##p(CPUSPARCState *env) + +#define F_BINOP(name)                                           \ +    float32 helper_f ## name ## s (CPUSPARCState *env, float32 src1, \ +                                   float32 src2)                \ +    {                                                           \ +        float32 ret;                                            \ +        clear_float_exceptions(env);                            \ +        ret = float32_ ## name (src1, src2, &env->fp_status);   \ +        check_ieee_exceptions(env);                             \ +        return ret;                                             \ +    }                                                           \ +    float64 helper_f ## name ## d (CPUSPARCState * env, float64 src1,\ +                                   float64 src2)                \ +    {                                                           \ +        float64 ret;                                            \ +        clear_float_exceptions(env);                            \ +        ret = float64_ ## name (src1, src2, &env->fp_status);   \ +        check_ieee_exceptions(env);                             \ +        return ret;                                             \ +    }                                                           \ +    F_HELPER(name, q)                                           \ +    {                                                           \ +        clear_float_exceptions(env);                            \ +        QT0 = float128_ ## name (QT0, QT1, &env->fp_status);    \ +        check_ieee_exceptions(env);                             \ +    } + +F_BINOP(add); +F_BINOP(sub); +F_BINOP(mul); +F_BINOP(div); +#undef F_BINOP + +float64 helper_fsmuld(CPUSPARCState *env, float32 src1, float32 src2) +{ +    float64 ret; +    clear_float_exceptions(env); +    ret = float64_mul(float32_to_float64(src1, &env->fp_status), +                      float32_to_float64(src2, &env->fp_status), +                      &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +void helper_fdmulq(CPUSPARCState *env, float64 src1, float64 src2) +{ +    clear_float_exceptions(env); +    QT0 = float128_mul(float64_to_float128(src1, &env->fp_status), +                       float64_to_float128(src2, &env->fp_status), +                       &env->fp_status); +    check_ieee_exceptions(env); +} + +float32 helper_fnegs(float32 src) +{ +    return float32_chs(src); +} + +#ifdef TARGET_SPARC64 +float64 helper_fnegd(float64 src) +{ +    return float64_chs(src); +} + +F_HELPER(neg, q) +{ +    QT0 = float128_chs(QT1); +} +#endif + +/* Integer to float conversion.  */ +float32 helper_fitos(CPUSPARCState *env, int32_t src) +{ +    /* Inexact error possible converting int to float.  */ +    float32 ret; +    clear_float_exceptions(env); +    ret = int32_to_float32(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +float64 helper_fitod(CPUSPARCState *env, int32_t src) +{ +    /* No possible exceptions converting int to double.  */ +    return int32_to_float64(src, &env->fp_status); +} + +void helper_fitoq(CPUSPARCState *env, int32_t src) +{ +    /* No possible exceptions converting int to long double.  */ +    QT0 = int32_to_float128(src, &env->fp_status); +} + +#ifdef TARGET_SPARC64 +float32 helper_fxtos(CPUSPARCState *env, int64_t src) +{ +    float32 ret; +    clear_float_exceptions(env); +    ret = int64_to_float32(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +float64 helper_fxtod(CPUSPARCState *env, int64_t src) +{ +    float64 ret; +    clear_float_exceptions(env); +    ret = int64_to_float64(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +void helper_fxtoq(CPUSPARCState *env, int64_t src) +{ +    /* No possible exceptions converting long long to long double.  */ +    QT0 = int64_to_float128(src, &env->fp_status); +} +#endif +#undef F_HELPER + +/* floating point conversion */ +float32 helper_fdtos(CPUSPARCState *env, float64 src) +{ +    float32 ret; +    clear_float_exceptions(env); +    ret = float64_to_float32(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +float64 helper_fstod(CPUSPARCState *env, float32 src) +{ +    float64 ret; +    clear_float_exceptions(env); +    ret = float32_to_float64(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +float32 helper_fqtos(CPUSPARCState *env) +{ +    float32 ret; +    clear_float_exceptions(env); +    ret = float128_to_float32(QT1, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +void helper_fstoq(CPUSPARCState *env, float32 src) +{ +    clear_float_exceptions(env); +    QT0 = float32_to_float128(src, &env->fp_status); +    check_ieee_exceptions(env); +} + +float64 helper_fqtod(CPUSPARCState *env) +{ +    float64 ret; +    clear_float_exceptions(env); +    ret = float128_to_float64(QT1, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +void helper_fdtoq(CPUSPARCState *env, float64 src) +{ +    clear_float_exceptions(env); +    QT0 = float64_to_float128(src, &env->fp_status); +    check_ieee_exceptions(env); +} + +/* Float to integer conversion.  */ +int32_t helper_fstoi(CPUSPARCState *env, float32 src) +{ +    int32_t ret; +    clear_float_exceptions(env); +    ret = float32_to_int32_round_to_zero(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +int32_t helper_fdtoi(CPUSPARCState *env, float64 src) +{ +    int32_t ret; +    clear_float_exceptions(env); +    ret = float64_to_int32_round_to_zero(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +int32_t helper_fqtoi(CPUSPARCState *env) +{ +    int32_t ret; +    clear_float_exceptions(env); +    ret = float128_to_int32_round_to_zero(QT1, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +#ifdef TARGET_SPARC64 +int64_t helper_fstox(CPUSPARCState *env, float32 src) +{ +    int64_t ret; +    clear_float_exceptions(env); +    ret = float32_to_int64_round_to_zero(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +int64_t helper_fdtox(CPUSPARCState *env, float64 src) +{ +    int64_t ret; +    clear_float_exceptions(env); +    ret = float64_to_int64_round_to_zero(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +int64_t helper_fqtox(CPUSPARCState *env) +{ +    int64_t ret; +    clear_float_exceptions(env); +    ret = float128_to_int64_round_to_zero(QT1, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} +#endif + +float32 helper_fabss(float32 src) +{ +    return float32_abs(src); +} + +#ifdef TARGET_SPARC64 +float64 helper_fabsd(float64 src) +{ +    return float64_abs(src); +} + +void helper_fabsq(CPUSPARCState *env) +{ +    QT0 = float128_abs(QT1); +} +#endif + +float32 helper_fsqrts(CPUSPARCState *env, float32 src) +{ +    float32 ret; +    clear_float_exceptions(env); +    ret = float32_sqrt(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +float64 helper_fsqrtd(CPUSPARCState *env, float64 src) +{ +    float64 ret; +    clear_float_exceptions(env); +    ret = float64_sqrt(src, &env->fp_status); +    check_ieee_exceptions(env); +    return ret; +} + +void helper_fsqrtq(CPUSPARCState *env) +{ +    clear_float_exceptions(env); +    QT0 = float128_sqrt(QT1, &env->fp_status); +    check_ieee_exceptions(env); +} + +#define GEN_FCMP(name, size, reg1, reg2, FS, E)                         \ +    void glue(helper_, name) (CPUSPARCState *env)                       \ +    {                                                                   \ +        int ret;                                                        \ +        clear_float_exceptions(env);                                    \ +        if (E) {                                                        \ +            ret = glue(size, _compare)(reg1, reg2, &env->fp_status);    \ +        } else {                                                        \ +            ret = glue(size, _compare_quiet)(reg1, reg2,                \ +                                             &env->fp_status);          \ +        }                                                               \ +        check_ieee_exceptions(env);                                     \ +        switch (ret) {                                                  \ +        case float_relation_unordered:                                  \ +            env->fsr |= (FSR_FCC1 | FSR_FCC0) << FS;                    \ +            env->fsr |= FSR_NVA;                                        \ +            break;                                                      \ +        case float_relation_less:                                       \ +            env->fsr &= ~(FSR_FCC1) << FS;                              \ +            env->fsr |= FSR_FCC0 << FS;                                 \ +            break;                                                      \ +        case float_relation_greater:                                    \ +            env->fsr &= ~(FSR_FCC0) << FS;                              \ +            env->fsr |= FSR_FCC1 << FS;                                 \ +            break;                                                      \ +        default:                                                        \ +            env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS);                 \ +            break;                                                      \ +        }                                                               \ +    } +#define GEN_FCMP_T(name, size, FS, E)                                   \ +    void glue(helper_, name)(CPUSPARCState *env, size src1, size src2)  \ +    {                                                                   \ +        int ret;                                                        \ +        clear_float_exceptions(env);                                    \ +        if (E) {                                                        \ +            ret = glue(size, _compare)(src1, src2, &env->fp_status);    \ +        } else {                                                        \ +            ret = glue(size, _compare_quiet)(src1, src2,                \ +                                             &env->fp_status);          \ +        }                                                               \ +        check_ieee_exceptions(env);                                     \ +        switch (ret) {                                                  \ +        case float_relation_unordered:                                  \ +            env->fsr |= (FSR_FCC1 | FSR_FCC0) << FS;                    \ +            break;                                                      \ +        case float_relation_less:                                       \ +            env->fsr &= ~(FSR_FCC1 << FS);                              \ +            env->fsr |= FSR_FCC0 << FS;                                 \ +            break;                                                      \ +        case float_relation_greater:                                    \ +            env->fsr &= ~(FSR_FCC0 << FS);                              \ +            env->fsr |= FSR_FCC1 << FS;                                 \ +            break;                                                      \ +        default:                                                        \ +            env->fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS);                 \ +            break;                                                      \ +        }                                                               \ +    } + +GEN_FCMP_T(fcmps, float32, 0, 0); +GEN_FCMP_T(fcmpd, float64, 0, 0); + +GEN_FCMP_T(fcmpes, float32, 0, 1); +GEN_FCMP_T(fcmped, float64, 0, 1); + +GEN_FCMP(fcmpq, float128, QT0, QT1, 0, 0); +GEN_FCMP(fcmpeq, float128, QT0, QT1, 0, 1); + +#ifdef TARGET_SPARC64 +GEN_FCMP_T(fcmps_fcc1, float32, 22, 0); +GEN_FCMP_T(fcmpd_fcc1, float64, 22, 0); +GEN_FCMP(fcmpq_fcc1, float128, QT0, QT1, 22, 0); + +GEN_FCMP_T(fcmps_fcc2, float32, 24, 0); +GEN_FCMP_T(fcmpd_fcc2, float64, 24, 0); +GEN_FCMP(fcmpq_fcc2, float128, QT0, QT1, 24, 0); + +GEN_FCMP_T(fcmps_fcc3, float32, 26, 0); +GEN_FCMP_T(fcmpd_fcc3, float64, 26, 0); +GEN_FCMP(fcmpq_fcc3, float128, QT0, QT1, 26, 0); + +GEN_FCMP_T(fcmpes_fcc1, float32, 22, 1); +GEN_FCMP_T(fcmped_fcc1, float64, 22, 1); +GEN_FCMP(fcmpeq_fcc1, float128, QT0, QT1, 22, 1); + +GEN_FCMP_T(fcmpes_fcc2, float32, 24, 1); +GEN_FCMP_T(fcmped_fcc2, float64, 24, 1); +GEN_FCMP(fcmpeq_fcc2, float128, QT0, QT1, 24, 1); + +GEN_FCMP_T(fcmpes_fcc3, float32, 26, 1); +GEN_FCMP_T(fcmped_fcc3, float64, 26, 1); +GEN_FCMP(fcmpeq_fcc3, float128, QT0, QT1, 26, 1); +#endif +#undef GEN_FCMP_T +#undef GEN_FCMP + +static inline void set_fsr(CPUSPARCState *env) +{ +    int rnd_mode; + +    switch (env->fsr & FSR_RD_MASK) { +    case FSR_RD_NEAREST: +        rnd_mode = float_round_nearest_even; +        break; +    default: +    case FSR_RD_ZERO: +        rnd_mode = float_round_to_zero; +        break; +    case FSR_RD_POS: +        rnd_mode = float_round_up; +        break; +    case FSR_RD_NEG: +        rnd_mode = float_round_down; +        break; +    } +    set_float_rounding_mode(rnd_mode, &env->fp_status); +} + +void helper_ldfsr(CPUSPARCState *env, uint32_t new_fsr) +{ +    env->fsr = (new_fsr & FSR_LDFSR_MASK) | (env->fsr & FSR_LDFSR_OLDMASK); +    set_fsr(env); +} + +#ifdef TARGET_SPARC64 +void helper_ldxfsr(CPUSPARCState *env, uint64_t new_fsr) +{ +    env->fsr = (new_fsr & FSR_LDXFSR_MASK) | (env->fsr & FSR_LDXFSR_OLDMASK); +    set_fsr(env); +} +#endif diff --git a/target-sparc/gdbstub.c b/target-sparc/gdbstub.c new file mode 100644 index 00000000..3de3242b --- /dev/null +++ b/target-sparc/gdbstub.c @@ -0,0 +1,208 @@ +/* + * SPARC 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" + +#ifdef TARGET_ABI32 +#define gdb_get_rega(buf, val) gdb_get_reg32(buf, val) +#else +#define gdb_get_rega(buf, val) gdb_get_regl(buf, val) +#endif + +int sparc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; + +    if (n < 8) { +        /* g0..g7 */ +        return gdb_get_rega(mem_buf, env->gregs[n]); +    } +    if (n < 32) { +        /* register window */ +        return gdb_get_rega(mem_buf, env->regwptr[n - 8]); +    } +#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64) +    if (n < 64) { +        /* fprs */ +        if (n & 1) { +            return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.lower); +        } else { +            return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.upper); +        } +    } +    /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ +    switch (n) { +    case 64: +        return gdb_get_rega(mem_buf, env->y); +    case 65: +        return gdb_get_rega(mem_buf, cpu_get_psr(env)); +    case 66: +        return gdb_get_rega(mem_buf, env->wim); +    case 67: +        return gdb_get_rega(mem_buf, env->tbr); +    case 68: +        return gdb_get_rega(mem_buf, env->pc); +    case 69: +        return gdb_get_rega(mem_buf, env->npc); +    case 70: +        return gdb_get_rega(mem_buf, env->fsr); +    case 71: +        return gdb_get_rega(mem_buf, 0); /* csr */ +    default: +        return gdb_get_rega(mem_buf, 0); +    } +#else +    if (n < 64) { +        /* f0-f31 */ +        if (n & 1) { +            return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.lower); +        } else { +            return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.upper); +        } +    } +    if (n < 80) { +        /* f32-f62 (double width, even numbers only) */ +        return gdb_get_reg64(mem_buf, env->fpr[(n - 32) / 2].ll); +    } +    switch (n) { +    case 80: +        return gdb_get_regl(mem_buf, env->pc); +    case 81: +        return gdb_get_regl(mem_buf, env->npc); +    case 82: +        return gdb_get_regl(mem_buf, (cpu_get_ccr(env) << 32) | +                                     ((env->asi & 0xff) << 24) | +                                     ((env->pstate & 0xfff) << 8) | +                                     cpu_get_cwp64(env)); +    case 83: +        return gdb_get_regl(mem_buf, env->fsr); +    case 84: +        return gdb_get_regl(mem_buf, env->fprs); +    case 85: +        return gdb_get_regl(mem_buf, env->y); +    } +#endif +    return 0; +} + +int sparc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; +#if defined(TARGET_ABI32) +    abi_ulong tmp; + +    tmp = ldl_p(mem_buf); +#else +    target_ulong tmp; + +    tmp = ldtul_p(mem_buf); +#endif + +    if (n < 8) { +        /* g0..g7 */ +        env->gregs[n] = tmp; +    } else if (n < 32) { +        /* register window */ +        env->regwptr[n - 8] = tmp; +    } +#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64) +    else if (n < 64) { +        /* fprs */ +        /* f0-f31 */ +        if (n & 1) { +            env->fpr[(n - 32) / 2].l.lower = tmp; +        } else { +            env->fpr[(n - 32) / 2].l.upper = tmp; +        } +    } else { +        /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ +        switch (n) { +        case 64: +            env->y = tmp; +            break; +        case 65: +            cpu_put_psr(env, tmp); +            break; +        case 66: +            env->wim = tmp; +            break; +        case 67: +            env->tbr = tmp; +            break; +        case 68: +            env->pc = tmp; +            break; +        case 69: +            env->npc = tmp; +            break; +        case 70: +            env->fsr = tmp; +            break; +        default: +            return 0; +        } +    } +    return 4; +#else +    else if (n < 64) { +        /* f0-f31 */ +        tmp = ldl_p(mem_buf); +        if (n & 1) { +            env->fpr[(n - 32) / 2].l.lower = tmp; +        } else { +            env->fpr[(n - 32) / 2].l.upper = tmp; +        } +        return 4; +    } else if (n < 80) { +        /* f32-f62 (double width, even numbers only) */ +        env->fpr[(n - 32) / 2].ll = tmp; +    } else { +        switch (n) { +        case 80: +            env->pc = tmp; +            break; +        case 81: +            env->npc = tmp; +            break; +        case 82: +            cpu_put_ccr(env, tmp >> 32); +            env->asi = (tmp >> 24) & 0xff; +            env->pstate = (tmp >> 8) & 0xfff; +            cpu_put_cwp64(env, tmp & 0xff); +            break; +        case 83: +            env->fsr = tmp; +            break; +        case 84: +            env->fprs = tmp; +            break; +        case 85: +            env->y = tmp; +            break; +        default: +            return 0; +        } +    } +    return 8; +#endif +} diff --git a/target-sparc/helper.c b/target-sparc/helper.c new file mode 100644 index 00000000..4850c7ce --- /dev/null +++ b/target-sparc/helper.c @@ -0,0 +1,255 @@ +/* + *  Misc Sparc helpers + * + *  Copyright (c) 2003-2005 Fabrice Bellard + * + * 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 "qemu/host-utils.h" +#include "exec/helper-proto.h" +#include "sysemu/sysemu.h" + +void helper_raise_exception(CPUSPARCState *env, int tt) +{ +    CPUState *cs = CPU(sparc_env_get_cpu(env)); + +    cs->exception_index = tt; +    cpu_loop_exit(cs); +} + +void helper_debug(CPUSPARCState *env) +{ +    CPUState *cs = CPU(sparc_env_get_cpu(env)); + +    cs->exception_index = EXCP_DEBUG; +    cpu_loop_exit(cs); +} + +#ifdef TARGET_SPARC64 +target_ulong helper_popc(target_ulong val) +{ +    return ctpop64(val); +} + +void helper_tick_set_count(void *opaque, uint64_t count) +{ +#if !defined(CONFIG_USER_ONLY) +    cpu_tick_set_count(opaque, count); +#endif +} + +uint64_t helper_tick_get_count(void *opaque) +{ +#if !defined(CONFIG_USER_ONLY) +    return cpu_tick_get_count(opaque); +#else +    return 0; +#endif +} + +void helper_tick_set_limit(void *opaque, uint64_t limit) +{ +#if !defined(CONFIG_USER_ONLY) +    cpu_tick_set_limit(opaque, limit); +#endif +} +#endif + +static target_ulong helper_udiv_common(CPUSPARCState *env, target_ulong a, +                                       target_ulong b, int cc) +{ +    SPARCCPU *cpu = sparc_env_get_cpu(env); +    int overflow = 0; +    uint64_t x0; +    uint32_t x1; + +    x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32); +    x1 = (b & 0xffffffff); + +    if (x1 == 0) { +        cpu_restore_state(CPU(cpu), GETPC()); +        helper_raise_exception(env, TT_DIV_ZERO); +    } + +    x0 = x0 / x1; +    if (x0 > UINT32_MAX) { +        x0 = UINT32_MAX; +        overflow = 1; +    } + +    if (cc) { +        env->cc_dst = x0; +        env->cc_src2 = overflow; +        env->cc_op = CC_OP_DIV; +    } +    return x0; +} + +target_ulong helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b) +{ +    return helper_udiv_common(env, a, b, 0); +} + +target_ulong helper_udiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b) +{ +    return helper_udiv_common(env, a, b, 1); +} + +static target_ulong helper_sdiv_common(CPUSPARCState *env, target_ulong a, +                                       target_ulong b, int cc) +{ +    SPARCCPU *cpu = sparc_env_get_cpu(env); +    int overflow = 0; +    int64_t x0; +    int32_t x1; + +    x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32); +    x1 = (b & 0xffffffff); + +    if (x1 == 0) { +        cpu_restore_state(CPU(cpu), GETPC()); +        helper_raise_exception(env, TT_DIV_ZERO); +    } else if (x1 == -1 && x0 == INT64_MIN) { +        x0 = INT32_MAX; +        overflow = 1; +    } else { +        x0 = x0 / x1; +        if ((int32_t) x0 != x0) { +            x0 = x0 < 0 ? INT32_MIN : INT32_MAX; +            overflow = 1; +        } +    } + +    if (cc) { +        env->cc_dst = x0; +        env->cc_src2 = overflow; +        env->cc_op = CC_OP_DIV; +    } +    return x0; +} + +target_ulong helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b) +{ +    return helper_sdiv_common(env, a, b, 0); +} + +target_ulong helper_sdiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b) +{ +    return helper_sdiv_common(env, a, b, 1); +} + +#ifdef TARGET_SPARC64 +int64_t helper_sdivx(CPUSPARCState *env, int64_t a, int64_t b) +{ +    if (b == 0) { +        /* Raise divide by zero trap.  */ +        SPARCCPU *cpu = sparc_env_get_cpu(env); + +        cpu_restore_state(CPU(cpu), GETPC()); +        helper_raise_exception(env, TT_DIV_ZERO); +    } else if (b == -1) { +        /* Avoid overflow trap with i386 divide insn.  */ +        return -a; +    } else { +        return a / b; +    } +} + +uint64_t helper_udivx(CPUSPARCState *env, uint64_t a, uint64_t b) +{ +    if (b == 0) { +        /* Raise divide by zero trap.  */ +        SPARCCPU *cpu = sparc_env_get_cpu(env); + +        cpu_restore_state(CPU(cpu), GETPC()); +        helper_raise_exception(env, TT_DIV_ZERO); +    } +    return a / b; +} +#endif + +target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1, +                             target_ulong src2) +{ +    SPARCCPU *cpu = sparc_env_get_cpu(env); +    target_ulong dst; + +    /* Tag overflow occurs if either input has bits 0 or 1 set.  */ +    if ((src1 | src2) & 3) { +        goto tag_overflow; +    } + +    dst = src1 + src2; + +    /* Tag overflow occurs if the addition overflows.  */ +    if (~(src1 ^ src2) & (src1 ^ dst) & (1u << 31)) { +        goto tag_overflow; +    } + +    /* Only modify the CC after any exceptions have been generated.  */ +    env->cc_op = CC_OP_TADDTV; +    env->cc_src = src1; +    env->cc_src2 = src2; +    env->cc_dst = dst; +    return dst; + + tag_overflow: +    cpu_restore_state(CPU(cpu), GETPC()); +    helper_raise_exception(env, TT_TOVF); +} + +target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1, +                             target_ulong src2) +{ +    SPARCCPU *cpu = sparc_env_get_cpu(env); +    target_ulong dst; + +    /* Tag overflow occurs if either input has bits 0 or 1 set.  */ +    if ((src1 | src2) & 3) { +        goto tag_overflow; +    } + +    dst = src1 - src2; + +    /* Tag overflow occurs if the subtraction overflows.  */ +    if ((src1 ^ src2) & (src1 ^ dst) & (1u << 31)) { +        goto tag_overflow; +    } + +    /* Only modify the CC after any exceptions have been generated.  */ +    env->cc_op = CC_OP_TSUBTV; +    env->cc_src = src1; +    env->cc_src2 = src2; +    env->cc_dst = dst; +    return dst; + + tag_overflow: +    cpu_restore_state(CPU(cpu), GETPC()); +    helper_raise_exception(env, TT_TOVF); +} + +#ifndef TARGET_SPARC64 +void helper_power_down(CPUSPARCState *env) +{ +    CPUState *cs = CPU(sparc_env_get_cpu(env)); + +    cs->halted = 1; +    cs->exception_index = EXCP_HLT; +    env->pc = env->npc; +    env->npc = env->pc + 4; +    cpu_loop_exit(cs); +} +#endif diff --git a/target-sparc/helper.h b/target-sparc/helper.h new file mode 100644 index 00000000..1ad23e8d --- /dev/null +++ b/target-sparc/helper.h @@ -0,0 +1,175 @@ +#ifndef TARGET_SPARC64 +DEF_HELPER_1(rett, void, env) +DEF_HELPER_2(wrpsr, void, env, tl) +DEF_HELPER_1(rdpsr, tl, env) +DEF_HELPER_1(power_down, void, env) +#else +DEF_HELPER_2(wrpil, void, env, tl) +DEF_HELPER_2(wrpstate, void, env, tl) +DEF_HELPER_1(done, void, env) +DEF_HELPER_1(retry, void, env) +DEF_HELPER_1(flushw, void, env) +DEF_HELPER_1(saved, void, env) +DEF_HELPER_1(restored, void, env) +DEF_HELPER_1(rdccr, tl, env) +DEF_HELPER_2(wrccr, void, env, tl) +DEF_HELPER_1(rdcwp, tl, env) +DEF_HELPER_2(wrcwp, void, env, tl) +DEF_HELPER_FLAGS_2(array8, TCG_CALL_NO_RWG_SE, tl, tl, tl) +DEF_HELPER_1(popc, tl, tl) +DEF_HELPER_4(ldda_asi, void, env, tl, int, int) +DEF_HELPER_5(ldf_asi, void, env, tl, int, int, int) +DEF_HELPER_5(stf_asi, void, env, tl, int, int, int) +DEF_HELPER_5(casx_asi, tl, env, tl, tl, tl, i32) +DEF_HELPER_2(set_softint, void, env, i64) +DEF_HELPER_2(clear_softint, void, env, i64) +DEF_HELPER_2(write_softint, void, env, i64) +DEF_HELPER_2(tick_set_count, void, ptr, i64) +DEF_HELPER_1(tick_get_count, i64, ptr) +DEF_HELPER_2(tick_set_limit, void, ptr, i64) +#endif +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) +DEF_HELPER_5(cas_asi, tl, env, tl, tl, tl, i32) +#endif +DEF_HELPER_3(check_align, void, env, tl, i32) +DEF_HELPER_1(debug, void, env) +DEF_HELPER_1(save, void, env) +DEF_HELPER_1(restore, void, env) +DEF_HELPER_3(udiv, tl, env, tl, tl) +DEF_HELPER_3(udiv_cc, tl, env, tl, tl) +DEF_HELPER_3(sdiv, tl, env, tl, tl) +DEF_HELPER_3(sdiv_cc, tl, env, tl, tl) +DEF_HELPER_3(taddcctv, tl, env, tl, tl) +DEF_HELPER_3(tsubcctv, tl, env, tl, tl) +#ifdef TARGET_SPARC64 +DEF_HELPER_3(sdivx, s64, env, s64, s64) +DEF_HELPER_3(udivx, i64, env, i64, i64) +#endif +DEF_HELPER_3(ldqf, void, env, tl, int) +DEF_HELPER_3(stqf, void, env, tl, int) +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) +DEF_HELPER_5(ld_asi, i64, env, tl, int, int, int) +DEF_HELPER_5(st_asi, void, env, tl, i64, int, int) +#endif +DEF_HELPER_2(ldfsr, void, env, i32) +DEF_HELPER_FLAGS_1(fabss, TCG_CALL_NO_RWG_SE, f32, f32) +DEF_HELPER_2(fsqrts, f32, env, f32) +DEF_HELPER_2(fsqrtd, f64, env, f64) +DEF_HELPER_3(fcmps, void, env, f32, f32) +DEF_HELPER_3(fcmpd, void, env, f64, f64) +DEF_HELPER_3(fcmpes, void, env, f32, f32) +DEF_HELPER_3(fcmped, void, env, f64, f64) +DEF_HELPER_1(fsqrtq, void, env) +DEF_HELPER_1(fcmpq, void, env) +DEF_HELPER_1(fcmpeq, void, env) +#ifdef TARGET_SPARC64 +DEF_HELPER_2(ldxfsr, void, env, i64) +DEF_HELPER_FLAGS_1(fabsd, TCG_CALL_NO_RWG_SE, f64, f64) +DEF_HELPER_3(fcmps_fcc1, void, env, f32, f32) +DEF_HELPER_3(fcmps_fcc2, void, env, f32, f32) +DEF_HELPER_3(fcmps_fcc3, void, env, f32, f32) +DEF_HELPER_3(fcmpd_fcc1, void, env, f64, f64) +DEF_HELPER_3(fcmpd_fcc2, void, env, f64, f64) +DEF_HELPER_3(fcmpd_fcc3, void, env, f64, f64) +DEF_HELPER_3(fcmpes_fcc1, void, env, f32, f32) +DEF_HELPER_3(fcmpes_fcc2, void, env, f32, f32) +DEF_HELPER_3(fcmpes_fcc3, void, env, f32, f32) +DEF_HELPER_3(fcmped_fcc1, void, env, f64, f64) +DEF_HELPER_3(fcmped_fcc2, void, env, f64, f64) +DEF_HELPER_3(fcmped_fcc3, void, env, f64, f64) +DEF_HELPER_1(fabsq, void, env) +DEF_HELPER_1(fcmpq_fcc1, void, env) +DEF_HELPER_1(fcmpq_fcc2, void, env) +DEF_HELPER_1(fcmpq_fcc3, void, env) +DEF_HELPER_1(fcmpeq_fcc1, void, env) +DEF_HELPER_1(fcmpeq_fcc2, void, env) +DEF_HELPER_1(fcmpeq_fcc3, void, env) +#endif +DEF_HELPER_2(raise_exception, noreturn, env, int) +#define F_HELPER_0_1(name) DEF_HELPER_1(f ## name, void, env) + +DEF_HELPER_3(faddd, f64, env, f64, f64) +DEF_HELPER_3(fsubd, f64, env, f64, f64) +DEF_HELPER_3(fmuld, f64, env, f64, f64) +DEF_HELPER_3(fdivd, f64, env, f64, f64) +F_HELPER_0_1(addq) +F_HELPER_0_1(subq) +F_HELPER_0_1(mulq) +F_HELPER_0_1(divq) + +DEF_HELPER_3(fadds, f32, env, f32, f32) +DEF_HELPER_3(fsubs, f32, env, f32, f32) +DEF_HELPER_3(fmuls, f32, env, f32, f32) +DEF_HELPER_3(fdivs, f32, env, f32, f32) + +DEF_HELPER_3(fsmuld, f64, env, f32, f32) +DEF_HELPER_3(fdmulq, void, env, f64, f64) + +DEF_HELPER_FLAGS_1(fnegs, TCG_CALL_NO_RWG_SE, f32, f32) +DEF_HELPER_2(fitod, f64, env, s32) +DEF_HELPER_2(fitoq, void, env, s32) + +DEF_HELPER_2(fitos, f32, env, s32) + +#ifdef TARGET_SPARC64 +DEF_HELPER_FLAGS_1(fnegd, TCG_CALL_NO_RWG_SE, f64, f64) +DEF_HELPER_1(fnegq, void, env) +DEF_HELPER_2(fxtos, f32, env, s64) +DEF_HELPER_2(fxtod, f64, env, s64) +DEF_HELPER_2(fxtoq, void, env, s64) +#endif +DEF_HELPER_2(fdtos, f32, env, f64) +DEF_HELPER_2(fstod, f64, env, f32) +DEF_HELPER_1(fqtos, f32, env) +DEF_HELPER_2(fstoq, void, env, f32) +DEF_HELPER_1(fqtod, f64, env) +DEF_HELPER_2(fdtoq, void, env, f64) +DEF_HELPER_2(fstoi, s32, env, f32) +DEF_HELPER_2(fdtoi, s32, env, f64) +DEF_HELPER_1(fqtoi, s32, env) +#ifdef TARGET_SPARC64 +DEF_HELPER_2(fstox, s64, env, f32) +DEF_HELPER_2(fdtox, s64, env, f64) +DEF_HELPER_1(fqtox, s64, env) + +DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmul8x16al, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmul8x16au, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmul8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmuld8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fmuld8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_2(fexpand, TCG_CALL_NO_RWG_SE, i64, i64, i64) +DEF_HELPER_FLAGS_3(pdist, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) +DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_NO_RWG_SE, i32, i64, i64) +DEF_HELPER_FLAGS_3(fpack32, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) +DEF_HELPER_FLAGS_2(fpackfix, TCG_CALL_NO_RWG_SE, i32, i64, i64) +DEF_HELPER_FLAGS_3(bshuffle, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64) +#define VIS_HELPER(name)                                                 \ +    DEF_HELPER_FLAGS_2(f ## name ## 16, TCG_CALL_NO_RWG_SE,  \ +                       i64, i64, i64)                                    \ +    DEF_HELPER_FLAGS_2(f ## name ## 16s, TCG_CALL_NO_RWG_SE, \ +                       i32, i32, i32)                                    \ +    DEF_HELPER_FLAGS_2(f ## name ## 32, TCG_CALL_NO_RWG_SE,  \ +                       i64, i64, i64)                                    \ +    DEF_HELPER_FLAGS_2(f ## name ## 32s, TCG_CALL_NO_RWG_SE, \ +                       i32, i32, i32) + +VIS_HELPER(padd) +VIS_HELPER(psub) +#define VIS_CMPHELPER(name)                                              \ +    DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE,      \ +                       i64, i64, i64)                                    \ +    DEF_HELPER_FLAGS_2(f##name##32, TCG_CALL_NO_RWG_SE,      \ +                       i64, i64, i64) +VIS_CMPHELPER(cmpgt) +VIS_CMPHELPER(cmpeq) +VIS_CMPHELPER(cmple) +VIS_CMPHELPER(cmpne) +#endif +#undef F_HELPER_0_1 +#undef VIS_HELPER +#undef VIS_CMPHELPER +DEF_HELPER_1(compute_psr, void, env) +DEF_HELPER_1(compute_C_icc, i32, env) diff --git a/target-sparc/int32_helper.c b/target-sparc/int32_helper.c new file mode 100644 index 00000000..7c380ba2 --- /dev/null +++ b/target-sparc/int32_helper.c @@ -0,0 +1,173 @@ +/* + * Sparc32 interrupt helpers + * + *  Copyright (c) 2003-2005 Fabrice Bellard + * + * 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 "trace.h" +#include "sysemu/sysemu.h" + +#define DEBUG_PCALL + +#ifdef DEBUG_PCALL +static const char * const excp_names[0x80] = { +    [TT_TFAULT] = "Instruction Access Fault", +    [TT_ILL_INSN] = "Illegal Instruction", +    [TT_PRIV_INSN] = "Privileged Instruction", +    [TT_NFPU_INSN] = "FPU Disabled", +    [TT_WIN_OVF] = "Window Overflow", +    [TT_WIN_UNF] = "Window Underflow", +    [TT_UNALIGNED] = "Unaligned Memory Access", +    [TT_FP_EXCP] = "FPU Exception", +    [TT_DFAULT] = "Data Access Fault", +    [TT_TOVF] = "Tag Overflow", +    [TT_EXTINT | 0x1] = "External Interrupt 1", +    [TT_EXTINT | 0x2] = "External Interrupt 2", +    [TT_EXTINT | 0x3] = "External Interrupt 3", +    [TT_EXTINT | 0x4] = "External Interrupt 4", +    [TT_EXTINT | 0x5] = "External Interrupt 5", +    [TT_EXTINT | 0x6] = "External Interrupt 6", +    [TT_EXTINT | 0x7] = "External Interrupt 7", +    [TT_EXTINT | 0x8] = "External Interrupt 8", +    [TT_EXTINT | 0x9] = "External Interrupt 9", +    [TT_EXTINT | 0xa] = "External Interrupt 10", +    [TT_EXTINT | 0xb] = "External Interrupt 11", +    [TT_EXTINT | 0xc] = "External Interrupt 12", +    [TT_EXTINT | 0xd] = "External Interrupt 13", +    [TT_EXTINT | 0xe] = "External Interrupt 14", +    [TT_EXTINT | 0xf] = "External Interrupt 15", +    [TT_TOVF] = "Tag Overflow", +    [TT_CODE_ACCESS] = "Instruction Access Error", +    [TT_DATA_ACCESS] = "Data Access Error", +    [TT_DIV_ZERO] = "Division By Zero", +    [TT_NCP_INSN] = "Coprocessor Disabled", +}; +#endif + +void sparc_cpu_do_interrupt(CPUState *cs) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; +    int cwp, intno = cs->exception_index; + +    /* Compute PSR before exposing state.  */ +    if (env->cc_op != CC_OP_FLAGS) { +        cpu_get_psr(env); +    } + +#ifdef DEBUG_PCALL +    if (qemu_loglevel_mask(CPU_LOG_INT)) { +        static int count; +        const char *name; + +        if (intno < 0 || intno >= 0x100) { +            name = "Unknown"; +        } else if (intno >= 0x80) { +            name = "Trap Instruction"; +        } else { +            name = excp_names[intno]; +            if (!name) { +                name = "Unknown"; +            } +        } + +        qemu_log("%6d: %s (v=%02x)\n", count, name, intno); +        log_cpu_state(cs, 0); +#if 0 +        { +            int i; +            uint8_t *ptr; + +            qemu_log("       code="); +            ptr = (uint8_t *)env->pc; +            for (i = 0; i < 16; i++) { +                qemu_log(" %02x", ldub(ptr + i)); +            } +            qemu_log("\n"); +        } +#endif +        count++; +    } +#endif +#if !defined(CONFIG_USER_ONLY) +    if (env->psret == 0) { +        if (cs->exception_index == 0x80 && +            env->def->features & CPU_FEATURE_TA0_SHUTDOWN) { +            qemu_system_shutdown_request(); +        } else { +            cpu_abort(cs, "Trap 0x%02x while interrupts disabled, Error state", +                      cs->exception_index); +        } +        return; +    } +#endif +    env->psret = 0; +    cwp = cpu_cwp_dec(env, env->cwp - 1); +    cpu_set_cwp(env, cwp); +    env->regwptr[9] = env->pc; +    env->regwptr[10] = env->npc; +    env->psrps = env->psrs; +    env->psrs = 1; +    env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); +    env->pc = env->tbr; +    env->npc = env->pc + 4; +    cs->exception_index = -1; + +#if !defined(CONFIG_USER_ONLY) +    /* IRQ acknowledgment */ +    if ((intno & ~15) == TT_EXTINT && env->qemu_irq_ack != NULL) { +        env->qemu_irq_ack(env, env->irq_manager, intno); +    } +#endif +} + +#if !defined(CONFIG_USER_ONLY) +static void leon3_cache_control_int(CPUSPARCState *env) +{ +    uint32_t state = 0; + +    if (env->cache_control & CACHE_CTRL_IF) { +        /* Instruction cache state */ +        state = env->cache_control & CACHE_STATE_MASK; +        if (state == CACHE_ENABLED) { +            state = CACHE_FROZEN; +            trace_int_helper_icache_freeze(); +        } + +        env->cache_control &= ~CACHE_STATE_MASK; +        env->cache_control |= state; +    } + +    if (env->cache_control & CACHE_CTRL_DF) { +        /* Data cache state */ +        state = (env->cache_control >> 2) & CACHE_STATE_MASK; +        if (state == CACHE_ENABLED) { +            state = CACHE_FROZEN; +            trace_int_helper_dcache_freeze(); +        } + +        env->cache_control &= ~(CACHE_STATE_MASK << 2); +        env->cache_control |= (state << 2); +    } +} + +void leon3_irq_manager(CPUSPARCState *env, void *irq_manager, int intno) +{ +    leon3_irq_ack(irq_manager, intno); +    leon3_cache_control_int(env); +} +#endif diff --git a/target-sparc/int64_helper.c b/target-sparc/int64_helper.c new file mode 100644 index 00000000..b02d22b1 --- /dev/null +++ b/target-sparc/int64_helper.c @@ -0,0 +1,204 @@ +/* + * Sparc64 interrupt helpers + * + *  Copyright (c) 2003-2005 Fabrice Bellard + * + * 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 "trace.h" + +#define DEBUG_PCALL + +#ifdef DEBUG_PCALL +static const char * const excp_names[0x80] = { +    [TT_TFAULT] = "Instruction Access Fault", +    [TT_TMISS] = "Instruction Access MMU Miss", +    [TT_CODE_ACCESS] = "Instruction Access Error", +    [TT_ILL_INSN] = "Illegal Instruction", +    [TT_PRIV_INSN] = "Privileged Instruction", +    [TT_NFPU_INSN] = "FPU Disabled", +    [TT_FP_EXCP] = "FPU Exception", +    [TT_TOVF] = "Tag Overflow", +    [TT_CLRWIN] = "Clean Windows", +    [TT_DIV_ZERO] = "Division By Zero", +    [TT_DFAULT] = "Data Access Fault", +    [TT_DMISS] = "Data Access MMU Miss", +    [TT_DATA_ACCESS] = "Data Access Error", +    [TT_DPROT] = "Data Protection Error", +    [TT_UNALIGNED] = "Unaligned Memory Access", +    [TT_PRIV_ACT] = "Privileged Action", +    [TT_EXTINT | 0x1] = "External Interrupt 1", +    [TT_EXTINT | 0x2] = "External Interrupt 2", +    [TT_EXTINT | 0x3] = "External Interrupt 3", +    [TT_EXTINT | 0x4] = "External Interrupt 4", +    [TT_EXTINT | 0x5] = "External Interrupt 5", +    [TT_EXTINT | 0x6] = "External Interrupt 6", +    [TT_EXTINT | 0x7] = "External Interrupt 7", +    [TT_EXTINT | 0x8] = "External Interrupt 8", +    [TT_EXTINT | 0x9] = "External Interrupt 9", +    [TT_EXTINT | 0xa] = "External Interrupt 10", +    [TT_EXTINT | 0xb] = "External Interrupt 11", +    [TT_EXTINT | 0xc] = "External Interrupt 12", +    [TT_EXTINT | 0xd] = "External Interrupt 13", +    [TT_EXTINT | 0xe] = "External Interrupt 14", +    [TT_EXTINT | 0xf] = "External Interrupt 15", +}; +#endif + +void sparc_cpu_do_interrupt(CPUState *cs) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; +    int intno = cs->exception_index; +    trap_state *tsptr; + +    /* Compute PSR before exposing state.  */ +    if (env->cc_op != CC_OP_FLAGS) { +        cpu_get_psr(env); +    } + +#ifdef DEBUG_PCALL +    if (qemu_loglevel_mask(CPU_LOG_INT)) { +        static int count; +        const char *name; + +        if (intno < 0 || intno >= 0x180) { +            name = "Unknown"; +        } else if (intno >= 0x100) { +            name = "Trap Instruction"; +        } else if (intno >= 0xc0) { +            name = "Window Fill"; +        } else if (intno >= 0x80) { +            name = "Window Spill"; +        } else { +            name = excp_names[intno]; +            if (!name) { +                name = "Unknown"; +            } +        } + +        qemu_log("%6d: %s (v=%04x)\n", count, name, intno); +        log_cpu_state(cs, 0); +#if 0 +        { +            int i; +            uint8_t *ptr; + +            qemu_log("       code="); +            ptr = (uint8_t *)env->pc; +            for (i = 0; i < 16; i++) { +                qemu_log(" %02x", ldub(ptr + i)); +            } +            qemu_log("\n"); +        } +#endif +        count++; +    } +#endif +#if !defined(CONFIG_USER_ONLY) +    if (env->tl >= env->maxtl) { +        cpu_abort(cs, "Trap 0x%04x while trap level (%d) >= MAXTL (%d)," +                  " Error state", cs->exception_index, env->tl, env->maxtl); +        return; +    } +#endif +    if (env->tl < env->maxtl - 1) { +        env->tl++; +    } else { +        env->pstate |= PS_RED; +        if (env->tl < env->maxtl) { +            env->tl++; +        } +    } +    tsptr = cpu_tsptr(env); + +    tsptr->tstate = (cpu_get_ccr(env) << 32) | +        ((env->asi & 0xff) << 24) | ((env->pstate & 0xf3f) << 8) | +        cpu_get_cwp64(env); +    tsptr->tpc = env->pc; +    tsptr->tnpc = env->npc; +    tsptr->tt = intno; + +    switch (intno) { +    case TT_IVEC: +        cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_IG); +        break; +    case TT_TFAULT: +    case TT_DFAULT: +    case TT_TMISS ... TT_TMISS + 3: +    case TT_DMISS ... TT_DMISS + 3: +    case TT_DPROT ... TT_DPROT + 3: +        cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_MG); +        break; +    default: +        cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_AG); +        break; +    } + +    if (intno == TT_CLRWIN) { +        cpu_set_cwp(env, cpu_cwp_dec(env, env->cwp - 1)); +    } else if ((intno & 0x1c0) == TT_SPILL) { +        cpu_set_cwp(env, cpu_cwp_dec(env, env->cwp - env->cansave - 2)); +    } else if ((intno & 0x1c0) == TT_FILL) { +        cpu_set_cwp(env, cpu_cwp_inc(env, env->cwp + 1)); +    } +    env->tbr &= ~0x7fffULL; +    env->tbr |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5); +    env->pc = env->tbr; +    env->npc = env->pc + 4; +    cs->exception_index = -1; +} + +trap_state *cpu_tsptr(CPUSPARCState* env) +{ +    return &env->ts[env->tl & MAXTL_MASK]; +} + +static bool do_modify_softint(CPUSPARCState *env, uint32_t value) +{ +    if (env->softint != value) { +        env->softint = value; +#if !defined(CONFIG_USER_ONLY) +        if (cpu_interrupts_enabled(env)) { +            cpu_check_irqs(env); +        } +#endif +        return true; +    } +    return false; +} + +void helper_set_softint(CPUSPARCState *env, uint64_t value) +{ +    if (do_modify_softint(env, env->softint | (uint32_t)value)) { +        trace_int_helper_set_softint(env->softint); +    } +} + +void helper_clear_softint(CPUSPARCState *env, uint64_t value) +{ +    if (do_modify_softint(env, env->softint & (uint32_t)~value)) { +        trace_int_helper_clear_softint(env->softint); +    } +} + +void helper_write_softint(CPUSPARCState *env, uint64_t value) +{ +    if (do_modify_softint(env, (uint32_t)value)) { +        trace_int_helper_write_softint(env->softint); +    } +} diff --git a/target-sparc/ldst_helper.c b/target-sparc/ldst_helper.c new file mode 100644 index 00000000..c7ad47d3 --- /dev/null +++ b/target-sparc/ldst_helper.c @@ -0,0 +1,2455 @@ +/* + * Helpers for loads and stores + * + *  Copyright (c) 2003-2005 Fabrice Bellard + * + * 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 "exec/cpu_ldst.h" + +//#define DEBUG_MMU +//#define DEBUG_MXCC +//#define DEBUG_UNALIGNED +//#define DEBUG_UNASSIGNED +//#define DEBUG_ASI +//#define DEBUG_CACHE_CONTROL + +#ifdef DEBUG_MMU +#define DPRINTF_MMU(fmt, ...)                                   \ +    do { printf("MMU: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF_MMU(fmt, ...) do {} while (0) +#endif + +#ifdef DEBUG_MXCC +#define DPRINTF_MXCC(fmt, ...)                                  \ +    do { printf("MXCC: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF_MXCC(fmt, ...) do {} while (0) +#endif + +#ifdef DEBUG_ASI +#define DPRINTF_ASI(fmt, ...)                                   \ +    do { printf("ASI: " fmt , ## __VA_ARGS__); } while (0) +#endif + +#ifdef DEBUG_CACHE_CONTROL +#define DPRINTF_CACHE_CONTROL(fmt, ...)                                 \ +    do { printf("CACHE_CONTROL: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF_CACHE_CONTROL(fmt, ...) do {} while (0) +#endif + +#ifdef TARGET_SPARC64 +#ifndef TARGET_ABI32 +#define AM_CHECK(env1) ((env1)->pstate & PS_AM) +#else +#define AM_CHECK(env1) (1) +#endif +#endif + +#define QT0 (env->qt0) +#define QT1 (env->qt1) + +#if defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) +/* Calculates TSB pointer value for fault page size 8k or 64k */ +static uint64_t ultrasparc_tsb_pointer(uint64_t tsb_register, +                                       uint64_t tag_access_register, +                                       int page_size) +{ +    uint64_t tsb_base = tsb_register & ~0x1fffULL; +    int tsb_split = (tsb_register & 0x1000ULL) ? 1 : 0; +    int tsb_size  = tsb_register & 0xf; + +    /* discard lower 13 bits which hold tag access context */ +    uint64_t tag_access_va = tag_access_register & ~0x1fffULL; + +    /* now reorder bits */ +    uint64_t tsb_base_mask = ~0x1fffULL; +    uint64_t va = tag_access_va; + +    /* move va bits to correct position */ +    if (page_size == 8*1024) { +        va >>= 9; +    } else if (page_size == 64*1024) { +        va >>= 12; +    } + +    if (tsb_size) { +        tsb_base_mask <<= tsb_size; +    } + +    /* calculate tsb_base mask and adjust va if split is in use */ +    if (tsb_split) { +        if (page_size == 8*1024) { +            va &= ~(1ULL << (13 + tsb_size)); +        } else if (page_size == 64*1024) { +            va |= (1ULL << (13 + tsb_size)); +        } +        tsb_base_mask <<= 1; +    } + +    return ((tsb_base & tsb_base_mask) | (va & ~tsb_base_mask)) & ~0xfULL; +} + +/* Calculates tag target register value by reordering bits +   in tag access register */ +static uint64_t ultrasparc_tag_target(uint64_t tag_access_register) +{ +    return ((tag_access_register & 0x1fff) << 48) | (tag_access_register >> 22); +} + +static void replace_tlb_entry(SparcTLBEntry *tlb, +                              uint64_t tlb_tag, uint64_t tlb_tte, +                              CPUSPARCState *env1) +{ +    target_ulong mask, size, va, offset; + +    /* flush page range if translation is valid */ +    if (TTE_IS_VALID(tlb->tte)) { +        CPUState *cs = CPU(sparc_env_get_cpu(env1)); + +        mask = 0xffffffffffffe000ULL; +        mask <<= 3 * ((tlb->tte >> 61) & 3); +        size = ~mask + 1; + +        va = tlb->tag & mask; + +        for (offset = 0; offset < size; offset += TARGET_PAGE_SIZE) { +            tlb_flush_page(cs, va + offset); +        } +    } + +    tlb->tag = tlb_tag; +    tlb->tte = tlb_tte; +} + +static void demap_tlb(SparcTLBEntry *tlb, target_ulong demap_addr, +                      const char *strmmu, CPUSPARCState *env1) +{ +    unsigned int i; +    target_ulong mask; +    uint64_t context; + +    int is_demap_context = (demap_addr >> 6) & 1; + +    /* demap context */ +    switch ((demap_addr >> 4) & 3) { +    case 0: /* primary */ +        context = env1->dmmu.mmu_primary_context; +        break; +    case 1: /* secondary */ +        context = env1->dmmu.mmu_secondary_context; +        break; +    case 2: /* nucleus */ +        context = 0; +        break; +    case 3: /* reserved */ +    default: +        return; +    } + +    for (i = 0; i < 64; i++) { +        if (TTE_IS_VALID(tlb[i].tte)) { + +            if (is_demap_context) { +                /* will remove non-global entries matching context value */ +                if (TTE_IS_GLOBAL(tlb[i].tte) || +                    !tlb_compare_context(&tlb[i], context)) { +                    continue; +                } +            } else { +                /* demap page +                   will remove any entry matching VA */ +                mask = 0xffffffffffffe000ULL; +                mask <<= 3 * ((tlb[i].tte >> 61) & 3); + +                if (!compare_masked(demap_addr, tlb[i].tag, mask)) { +                    continue; +                } + +                /* entry should be global or matching context value */ +                if (!TTE_IS_GLOBAL(tlb[i].tte) && +                    !tlb_compare_context(&tlb[i], context)) { +                    continue; +                } +            } + +            replace_tlb_entry(&tlb[i], 0, 0, env1); +#ifdef DEBUG_MMU +            DPRINTF_MMU("%s demap invalidated entry [%02u]\n", strmmu, i); +            dump_mmu(stdout, fprintf, env1); +#endif +        } +    } +} + +static void replace_tlb_1bit_lru(SparcTLBEntry *tlb, +                                 uint64_t tlb_tag, uint64_t tlb_tte, +                                 const char *strmmu, CPUSPARCState *env1) +{ +    unsigned int i, replace_used; + +    /* Try replacing invalid entry */ +    for (i = 0; i < 64; i++) { +        if (!TTE_IS_VALID(tlb[i].tte)) { +            replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1); +#ifdef DEBUG_MMU +            DPRINTF_MMU("%s lru replaced invalid entry [%i]\n", strmmu, i); +            dump_mmu(stdout, fprintf, env1); +#endif +            return; +        } +    } + +    /* All entries are valid, try replacing unlocked entry */ + +    for (replace_used = 0; replace_used < 2; ++replace_used) { + +        /* Used entries are not replaced on first pass */ + +        for (i = 0; i < 64; i++) { +            if (!TTE_IS_LOCKED(tlb[i].tte) && !TTE_IS_USED(tlb[i].tte)) { + +                replace_tlb_entry(&tlb[i], tlb_tag, tlb_tte, env1); +#ifdef DEBUG_MMU +                DPRINTF_MMU("%s lru replaced unlocked %s entry [%i]\n", +                            strmmu, (replace_used ? "used" : "unused"), i); +                dump_mmu(stdout, fprintf, env1); +#endif +                return; +            } +        } + +        /* Now reset used bit and search for unused entries again */ + +        for (i = 0; i < 64; i++) { +            TTE_SET_UNUSED(tlb[i].tte); +        } +    } + +#ifdef DEBUG_MMU +    DPRINTF_MMU("%s lru replacement failed: no entries available\n", strmmu); +#endif +    /* error state? */ +} + +#endif + +#if defined(TARGET_SPARC64) || defined(CONFIG_USER_ONLY) +static inline target_ulong address_mask(CPUSPARCState *env1, target_ulong addr) +{ +#ifdef TARGET_SPARC64 +    if (AM_CHECK(env1)) { +        addr &= 0xffffffffULL; +    } +#endif +    return addr; +} +#endif + +#ifdef TARGET_SPARC64 +/* returns true if access using this ASI is to have address translated by MMU +   otherwise access is to raw physical address */ +/* TODO: check sparc32 bits */ +static inline int is_translating_asi(int asi) +{ +    /* Ultrasparc IIi translating asi +       - note this list is defined by cpu implementation +    */ +    switch (asi) { +    case 0x04 ... 0x11: +    case 0x16 ... 0x19: +    case 0x1E ... 0x1F: +    case 0x24 ... 0x2C: +    case 0x70 ... 0x73: +    case 0x78 ... 0x79: +    case 0x80 ... 0xFF: +        return 1; + +    default: +        return 0; +    } +} + +static inline target_ulong asi_address_mask(CPUSPARCState *env, +                                            int asi, target_ulong addr) +{ +    if (is_translating_asi(asi)) { +        return address_mask(env, addr); +    } else { +        return addr; +    } +} +#endif + +void helper_check_align(CPUSPARCState *env, target_ulong addr, uint32_t align) +{ +    if (addr & align) { +#ifdef DEBUG_UNALIGNED +        printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx +               "\n", addr, env->pc); +#endif +        helper_raise_exception(env, TT_UNALIGNED); +    } +} + +#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) &&   \ +    defined(DEBUG_MXCC) +static void dump_mxcc(CPUSPARCState *env) +{ +    printf("mxccdata: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 +           "\n", +           env->mxccdata[0], env->mxccdata[1], +           env->mxccdata[2], env->mxccdata[3]); +    printf("mxccregs: %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 +           "\n" +           "          %016" PRIx64 " %016" PRIx64 " %016" PRIx64 " %016" PRIx64 +           "\n", +           env->mxccregs[0], env->mxccregs[1], +           env->mxccregs[2], env->mxccregs[3], +           env->mxccregs[4], env->mxccregs[5], +           env->mxccregs[6], env->mxccregs[7]); +} +#endif + +#if (defined(TARGET_SPARC64) || !defined(CONFIG_USER_ONLY))     \ +    && defined(DEBUG_ASI) +static void dump_asi(const char *txt, target_ulong addr, int asi, int size, +                     uint64_t r1) +{ +    switch (size) { +    case 1: +        DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %02" PRIx64 "\n", txt, +                    addr, asi, r1 & 0xff); +        break; +    case 2: +        DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %04" PRIx64 "\n", txt, +                    addr, asi, r1 & 0xffff); +        break; +    case 4: +        DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %08" PRIx64 "\n", txt, +                    addr, asi, r1 & 0xffffffff); +        break; +    case 8: +        DPRINTF_ASI("%s "TARGET_FMT_lx " asi 0x%02x = %016" PRIx64 "\n", txt, +                    addr, asi, r1); +        break; +    } +} +#endif + +#ifndef TARGET_SPARC64 +#ifndef CONFIG_USER_ONLY + + +/* Leon3 cache control */ + +static void leon3_cache_control_st(CPUSPARCState *env, target_ulong addr, +                                   uint64_t val, int size) +{ +    DPRINTF_CACHE_CONTROL("st addr:%08x, val:%" PRIx64 ", size:%d\n", +                          addr, val, size); + +    if (size != 4) { +        DPRINTF_CACHE_CONTROL("32bits only\n"); +        return; +    } + +    switch (addr) { +    case 0x00:              /* Cache control */ + +        /* These values must always be read as zeros */ +        val &= ~CACHE_CTRL_FD; +        val &= ~CACHE_CTRL_FI; +        val &= ~CACHE_CTRL_IB; +        val &= ~CACHE_CTRL_IP; +        val &= ~CACHE_CTRL_DP; + +        env->cache_control = val; +        break; +    case 0x04:              /* Instruction cache configuration */ +    case 0x08:              /* Data cache configuration */ +        /* Read Only */ +        break; +    default: +        DPRINTF_CACHE_CONTROL("write unknown register %08x\n", addr); +        break; +    }; +} + +static uint64_t leon3_cache_control_ld(CPUSPARCState *env, target_ulong addr, +                                       int size) +{ +    uint64_t ret = 0; + +    if (size != 4) { +        DPRINTF_CACHE_CONTROL("32bits only\n"); +        return 0; +    } + +    switch (addr) { +    case 0x00:              /* Cache control */ +        ret = env->cache_control; +        break; + +        /* Configuration registers are read and only always keep those +           predefined values */ + +    case 0x04:              /* Instruction cache configuration */ +        ret = 0x10220000; +        break; +    case 0x08:              /* Data cache configuration */ +        ret = 0x18220000; +        break; +    default: +        DPRINTF_CACHE_CONTROL("read unknown register %08x\n", addr); +        break; +    }; +    DPRINTF_CACHE_CONTROL("ld addr:%08x, ret:0x%" PRIx64 ", size:%d\n", +                          addr, ret, size); +    return ret; +} + +uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int asi, int size, +                       int sign) +{ +    CPUState *cs = CPU(sparc_env_get_cpu(env)); +    uint64_t ret = 0; +#if defined(DEBUG_MXCC) || defined(DEBUG_ASI) +    uint32_t last_addr = addr; +#endif + +    helper_check_align(env, addr, size - 1); +    switch (asi) { +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */ +        switch (addr) { +        case 0x00:          /* Leon3 Cache Control */ +        case 0x08:          /* Leon3 Instruction Cache config */ +        case 0x0C:          /* Leon3 Date Cache config */ +            if (env->def->features & CPU_FEATURE_CACHE_CTRL) { +                ret = leon3_cache_control_ld(env, addr, size); +            } +            break; +        case 0x01c00a00: /* MXCC control register */ +            if (size == 8) { +                ret = env->mxccregs[3]; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        case 0x01c00a04: /* MXCC control register */ +            if (size == 4) { +                ret = env->mxccregs[3]; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        case 0x01c00c00: /* Module reset register */ +            if (size == 8) { +                ret = env->mxccregs[5]; +                /* should we do something here? */ +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        case 0x01c00f00: /* MBus port address register */ +            if (size == 8) { +                ret = env->mxccregs[7]; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        default: +            qemu_log_mask(LOG_UNIMP, +                          "%08x: unimplemented address, size: %d\n", addr, +                          size); +            break; +        } +        DPRINTF_MXCC("asi = %d, size = %d, sign = %d, " +                     "addr = %08x -> ret = %" PRIx64 "," +                     "addr = %08x\n", asi, size, sign, last_addr, ret, addr); +#ifdef DEBUG_MXCC +        dump_mxcc(env); +#endif +        break; +    case 3: /* MMU probe */ +    case 0x18: /* LEON3 MMU probe */ +        { +            int mmulev; + +            mmulev = (addr >> 8) & 15; +            if (mmulev > 4) { +                ret = 0; +            } else { +                ret = mmu_probe(env, addr, mmulev); +            } +            DPRINTF_MMU("mmu_probe: 0x%08x (lev %d) -> 0x%08" PRIx64 "\n", +                        addr, mmulev, ret); +        } +        break; +    case 4: /* read MMU regs */ +    case 0x19: /* LEON3 read MMU regs */ +        { +            int reg = (addr >> 8) & 0x1f; + +            ret = env->mmuregs[reg]; +            if (reg == 3) { /* Fault status cleared on read */ +                env->mmuregs[3] = 0; +            } else if (reg == 0x13) { /* Fault status read */ +                ret = env->mmuregs[3]; +            } else if (reg == 0x14) { /* Fault address read */ +                ret = env->mmuregs[4]; +            } +            DPRINTF_MMU("mmu_read: reg[%d] = 0x%08" PRIx64 "\n", reg, ret); +        } +        break; +    case 5: /* Turbosparc ITLB Diagnostic */ +    case 6: /* Turbosparc DTLB Diagnostic */ +    case 7: /* Turbosparc IOTLB Diagnostic */ +        break; +    case 9: /* Supervisor code access */ +        switch (size) { +        case 1: +            ret = cpu_ldub_code(env, addr); +            break; +        case 2: +            ret = cpu_lduw_code(env, addr); +            break; +        default: +        case 4: +            ret = cpu_ldl_code(env, addr); +            break; +        case 8: +            ret = cpu_ldq_code(env, addr); +            break; +        } +        break; +    case 0xa: /* User data access */ +        switch (size) { +        case 1: +            ret = cpu_ldub_user(env, addr); +            break; +        case 2: +            ret = cpu_lduw_user(env, addr); +            break; +        default: +        case 4: +            ret = cpu_ldl_user(env, addr); +            break; +        case 8: +            ret = cpu_ldq_user(env, addr); +            break; +        } +        break; +    case 0xb: /* Supervisor data access */ +    case 0x80: +        switch (size) { +        case 1: +            ret = cpu_ldub_kernel(env, addr); +            break; +        case 2: +            ret = cpu_lduw_kernel(env, addr); +            break; +        default: +        case 4: +            ret = cpu_ldl_kernel(env, addr); +            break; +        case 8: +            ret = cpu_ldq_kernel(env, addr); +            break; +        } +        break; +    case 0xc: /* I-cache tag */ +    case 0xd: /* I-cache data */ +    case 0xe: /* D-cache tag */ +    case 0xf: /* D-cache data */ +        break; +    case 0x20: /* MMU passthrough */ +    case 0x1c: /* LEON MMU passthrough */ +        switch (size) { +        case 1: +            ret = ldub_phys(cs->as, addr); +            break; +        case 2: +            ret = lduw_phys(cs->as, addr); +            break; +        default: +        case 4: +            ret = ldl_phys(cs->as, addr); +            break; +        case 8: +            ret = ldq_phys(cs->as, addr); +            break; +        } +        break; +    case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */ +        switch (size) { +        case 1: +            ret = ldub_phys(cs->as, (hwaddr)addr +                            | ((hwaddr)(asi & 0xf) << 32)); +            break; +        case 2: +            ret = lduw_phys(cs->as, (hwaddr)addr +                            | ((hwaddr)(asi & 0xf) << 32)); +            break; +        default: +        case 4: +            ret = ldl_phys(cs->as, (hwaddr)addr +                           | ((hwaddr)(asi & 0xf) << 32)); +            break; +        case 8: +            ret = ldq_phys(cs->as, (hwaddr)addr +                           | ((hwaddr)(asi & 0xf) << 32)); +            break; +        } +        break; +    case 0x30: /* Turbosparc secondary cache diagnostic */ +    case 0x31: /* Turbosparc RAM snoop */ +    case 0x32: /* Turbosparc page table descriptor diagnostic */ +    case 0x39: /* data cache diagnostic register */ +        ret = 0; +        break; +    case 0x38: /* SuperSPARC MMU Breakpoint Control Registers */ +        { +            int reg = (addr >> 8) & 3; + +            switch (reg) { +            case 0: /* Breakpoint Value (Addr) */ +                ret = env->mmubpregs[reg]; +                break; +            case 1: /* Breakpoint Mask */ +                ret = env->mmubpregs[reg]; +                break; +            case 2: /* Breakpoint Control */ +                ret = env->mmubpregs[reg]; +                break; +            case 3: /* Breakpoint Status */ +                ret = env->mmubpregs[reg]; +                env->mmubpregs[reg] = 0ULL; +                break; +            } +            DPRINTF_MMU("read breakpoint reg[%d] 0x%016" PRIx64 "\n", reg, +                        ret); +        } +        break; +    case 0x49: /* SuperSPARC MMU Counter Breakpoint Value */ +        ret = env->mmubpctrv; +        break; +    case 0x4a: /* SuperSPARC MMU Counter Breakpoint Control */ +        ret = env->mmubpctrc; +        break; +    case 0x4b: /* SuperSPARC MMU Counter Breakpoint Status */ +        ret = env->mmubpctrs; +        break; +    case 0x4c: /* SuperSPARC MMU Breakpoint Action */ +        ret = env->mmubpaction; +        break; +    case 8: /* User code access, XXX */ +    default: +        cpu_unassigned_access(cs, addr, false, false, asi, size); +        ret = 0; +        break; +    } +    if (sign) { +        switch (size) { +        case 1: +            ret = (int8_t) ret; +            break; +        case 2: +            ret = (int16_t) ret; +            break; +        case 4: +            ret = (int32_t) ret; +            break; +        default: +            break; +        } +    } +#ifdef DEBUG_ASI +    dump_asi("read ", last_addr, asi, size, ret); +#endif +    return ret; +} + +void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val, int asi, +                   int size) +{ +    SPARCCPU *cpu = sparc_env_get_cpu(env); +    CPUState *cs = CPU(cpu); + +    helper_check_align(env, addr, size - 1); +    switch (asi) { +    case 2: /* SuperSparc MXCC registers and Leon3 cache control */ +        switch (addr) { +        case 0x00:          /* Leon3 Cache Control */ +        case 0x08:          /* Leon3 Instruction Cache config */ +        case 0x0C:          /* Leon3 Date Cache config */ +            if (env->def->features & CPU_FEATURE_CACHE_CTRL) { +                leon3_cache_control_st(env, addr, val, size); +            } +            break; + +        case 0x01c00000: /* MXCC stream data register 0 */ +            if (size == 8) { +                env->mxccdata[0] = val; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        case 0x01c00008: /* MXCC stream data register 1 */ +            if (size == 8) { +                env->mxccdata[1] = val; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        case 0x01c00010: /* MXCC stream data register 2 */ +            if (size == 8) { +                env->mxccdata[2] = val; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        case 0x01c00018: /* MXCC stream data register 3 */ +            if (size == 8) { +                env->mxccdata[3] = val; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        case 0x01c00100: /* MXCC stream source */ +            if (size == 8) { +                env->mxccregs[0] = val; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            env->mxccdata[0] = ldq_phys(cs->as, +                                        (env->mxccregs[0] & 0xffffffffULL) + +                                        0); +            env->mxccdata[1] = ldq_phys(cs->as, +                                        (env->mxccregs[0] & 0xffffffffULL) + +                                        8); +            env->mxccdata[2] = ldq_phys(cs->as, +                                        (env->mxccregs[0] & 0xffffffffULL) + +                                        16); +            env->mxccdata[3] = ldq_phys(cs->as, +                                        (env->mxccregs[0] & 0xffffffffULL) + +                                        24); +            break; +        case 0x01c00200: /* MXCC stream destination */ +            if (size == 8) { +                env->mxccregs[1] = val; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            stq_phys(cs->as, (env->mxccregs[1] & 0xffffffffULL) +  0, +                     env->mxccdata[0]); +            stq_phys(cs->as, (env->mxccregs[1] & 0xffffffffULL) +  8, +                     env->mxccdata[1]); +            stq_phys(cs->as, (env->mxccregs[1] & 0xffffffffULL) + 16, +                     env->mxccdata[2]); +            stq_phys(cs->as, (env->mxccregs[1] & 0xffffffffULL) + 24, +                     env->mxccdata[3]); +            break; +        case 0x01c00a00: /* MXCC control register */ +            if (size == 8) { +                env->mxccregs[3] = val; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        case 0x01c00a04: /* MXCC control register */ +            if (size == 4) { +                env->mxccregs[3] = (env->mxccregs[3] & 0xffffffff00000000ULL) +                    | val; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        case 0x01c00e00: /* MXCC error register  */ +            /* writing a 1 bit clears the error */ +            if (size == 8) { +                env->mxccregs[6] &= ~val; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        case 0x01c00f00: /* MBus port address register */ +            if (size == 8) { +                env->mxccregs[7] = val; +            } else { +                qemu_log_mask(LOG_UNIMP, +                              "%08x: unimplemented access size: %d\n", addr, +                              size); +            } +            break; +        default: +            qemu_log_mask(LOG_UNIMP, +                          "%08x: unimplemented address, size: %d\n", addr, +                          size); +            break; +        } +        DPRINTF_MXCC("asi = %d, size = %d, addr = %08x, val = %" PRIx64 "\n", +                     asi, size, addr, val); +#ifdef DEBUG_MXCC +        dump_mxcc(env); +#endif +        break; +    case 3: /* MMU flush */ +    case 0x18: /* LEON3 MMU flush */ +        { +            int mmulev; + +            mmulev = (addr >> 8) & 15; +            DPRINTF_MMU("mmu flush level %d\n", mmulev); +            switch (mmulev) { +            case 0: /* flush page */ +                tlb_flush_page(CPU(cpu), addr & 0xfffff000); +                break; +            case 1: /* flush segment (256k) */ +            case 2: /* flush region (16M) */ +            case 3: /* flush context (4G) */ +            case 4: /* flush entire */ +                tlb_flush(CPU(cpu), 1); +                break; +            default: +                break; +            } +#ifdef DEBUG_MMU +            dump_mmu(stdout, fprintf, env); +#endif +        } +        break; +    case 4: /* write MMU regs */ +    case 0x19: /* LEON3 write MMU regs */ +        { +            int reg = (addr >> 8) & 0x1f; +            uint32_t oldreg; + +            oldreg = env->mmuregs[reg]; +            switch (reg) { +            case 0: /* Control Register */ +                env->mmuregs[reg] = (env->mmuregs[reg] & 0xff000000) | +                    (val & 0x00ffffff); +                /* Mappings generated during no-fault mode or MMU +                   disabled mode are invalid in normal mode */ +                if ((oldreg & (MMU_E | MMU_NF | env->def->mmu_bm)) != +                    (env->mmuregs[reg] & (MMU_E | MMU_NF | env->def->mmu_bm))) { +                    tlb_flush(CPU(cpu), 1); +                } +                break; +            case 1: /* Context Table Pointer Register */ +                env->mmuregs[reg] = val & env->def->mmu_ctpr_mask; +                break; +            case 2: /* Context Register */ +                env->mmuregs[reg] = val & env->def->mmu_cxr_mask; +                if (oldreg != env->mmuregs[reg]) { +                    /* we flush when the MMU context changes because +                       QEMU has no MMU context support */ +                    tlb_flush(CPU(cpu), 1); +                } +                break; +            case 3: /* Synchronous Fault Status Register with Clear */ +            case 4: /* Synchronous Fault Address Register */ +                break; +            case 0x10: /* TLB Replacement Control Register */ +                env->mmuregs[reg] = val & env->def->mmu_trcr_mask; +                break; +            case 0x13: /* Synchronous Fault Status Register with Read +                          and Clear */ +                env->mmuregs[3] = val & env->def->mmu_sfsr_mask; +                break; +            case 0x14: /* Synchronous Fault Address Register */ +                env->mmuregs[4] = val; +                break; +            default: +                env->mmuregs[reg] = val; +                break; +            } +            if (oldreg != env->mmuregs[reg]) { +                DPRINTF_MMU("mmu change reg[%d]: 0x%08x -> 0x%08x\n", +                            reg, oldreg, env->mmuregs[reg]); +            } +#ifdef DEBUG_MMU +            dump_mmu(stdout, fprintf, env); +#endif +        } +        break; +    case 5: /* Turbosparc ITLB Diagnostic */ +    case 6: /* Turbosparc DTLB Diagnostic */ +    case 7: /* Turbosparc IOTLB Diagnostic */ +        break; +    case 0xa: /* User data access */ +        switch (size) { +        case 1: +            cpu_stb_user(env, addr, val); +            break; +        case 2: +            cpu_stw_user(env, addr, val); +            break; +        default: +        case 4: +            cpu_stl_user(env, addr, val); +            break; +        case 8: +            cpu_stq_user(env, addr, val); +            break; +        } +        break; +    case 0xb: /* Supervisor data access */ +    case 0x80: +        switch (size) { +        case 1: +            cpu_stb_kernel(env, addr, val); +            break; +        case 2: +            cpu_stw_kernel(env, addr, val); +            break; +        default: +        case 4: +            cpu_stl_kernel(env, addr, val); +            break; +        case 8: +            cpu_stq_kernel(env, addr, val); +            break; +        } +        break; +    case 0xc: /* I-cache tag */ +    case 0xd: /* I-cache data */ +    case 0xe: /* D-cache tag */ +    case 0xf: /* D-cache data */ +    case 0x10: /* I/D-cache flush page */ +    case 0x11: /* I/D-cache flush segment */ +    case 0x12: /* I/D-cache flush region */ +    case 0x13: /* I/D-cache flush context */ +    case 0x14: /* I/D-cache flush user */ +        break; +    case 0x17: /* Block copy, sta access */ +        { +            /* val = src +               addr = dst +               copy 32 bytes */ +            unsigned int i; +            uint32_t src = val & ~3, dst = addr & ~3, temp; + +            for (i = 0; i < 32; i += 4, src += 4, dst += 4) { +                temp = cpu_ldl_kernel(env, src); +                cpu_stl_kernel(env, dst, temp); +            } +        } +        break; +    case 0x1f: /* Block fill, stda access */ +        { +            /* addr = dst +               fill 32 bytes with val */ +            unsigned int i; +            uint32_t dst = addr & 7; + +            for (i = 0; i < 32; i += 8, dst += 8) { +                cpu_stq_kernel(env, dst, val); +            } +        } +        break; +    case 0x20: /* MMU passthrough */ +    case 0x1c: /* LEON MMU passthrough */ +        { +            switch (size) { +            case 1: +                stb_phys(cs->as, addr, val); +                break; +            case 2: +                stw_phys(cs->as, addr, val); +                break; +            case 4: +            default: +                stl_phys(cs->as, addr, val); +                break; +            case 8: +                stq_phys(cs->as, addr, val); +                break; +            } +        } +        break; +    case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */ +        { +            switch (size) { +            case 1: +                stb_phys(cs->as, (hwaddr)addr +                         | ((hwaddr)(asi & 0xf) << 32), val); +                break; +            case 2: +                stw_phys(cs->as, (hwaddr)addr +                         | ((hwaddr)(asi & 0xf) << 32), val); +                break; +            case 4: +            default: +                stl_phys(cs->as, (hwaddr)addr +                         | ((hwaddr)(asi & 0xf) << 32), val); +                break; +            case 8: +                stq_phys(cs->as, (hwaddr)addr +                         | ((hwaddr)(asi & 0xf) << 32), val); +                break; +            } +        } +        break; +    case 0x30: /* store buffer tags or Turbosparc secondary cache diagnostic */ +    case 0x31: /* store buffer data, Ross RT620 I-cache flush or +                  Turbosparc snoop RAM */ +    case 0x32: /* store buffer control or Turbosparc page table +                  descriptor diagnostic */ +    case 0x36: /* I-cache flash clear */ +    case 0x37: /* D-cache flash clear */ +        break; +    case 0x38: /* SuperSPARC MMU Breakpoint Control Registers*/ +        { +            int reg = (addr >> 8) & 3; + +            switch (reg) { +            case 0: /* Breakpoint Value (Addr) */ +                env->mmubpregs[reg] = (val & 0xfffffffffULL); +                break; +            case 1: /* Breakpoint Mask */ +                env->mmubpregs[reg] = (val & 0xfffffffffULL); +                break; +            case 2: /* Breakpoint Control */ +                env->mmubpregs[reg] = (val & 0x7fULL); +                break; +            case 3: /* Breakpoint Status */ +                env->mmubpregs[reg] = (val & 0xfULL); +                break; +            } +            DPRINTF_MMU("write breakpoint reg[%d] 0x%016x\n", reg, +                        env->mmuregs[reg]); +        } +        break; +    case 0x49: /* SuperSPARC MMU Counter Breakpoint Value */ +        env->mmubpctrv = val & 0xffffffff; +        break; +    case 0x4a: /* SuperSPARC MMU Counter Breakpoint Control */ +        env->mmubpctrc = val & 0x3; +        break; +    case 0x4b: /* SuperSPARC MMU Counter Breakpoint Status */ +        env->mmubpctrs = val & 0x3; +        break; +    case 0x4c: /* SuperSPARC MMU Breakpoint Action */ +        env->mmubpaction = val & 0x1fff; +        break; +    case 8: /* User code access, XXX */ +    case 9: /* Supervisor code access, XXX */ +    default: +        cpu_unassigned_access(CPU(sparc_env_get_cpu(env)), +                              addr, true, false, asi, size); +        break; +    } +#ifdef DEBUG_ASI +    dump_asi("write", addr, asi, size, val); +#endif +} + +#endif /* CONFIG_USER_ONLY */ +#else /* TARGET_SPARC64 */ + +#ifdef CONFIG_USER_ONLY +uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int asi, int size, +                       int sign) +{ +    uint64_t ret = 0; +#if defined(DEBUG_ASI) +    target_ulong last_addr = addr; +#endif + +    if (asi < 0x80) { +        helper_raise_exception(env, TT_PRIV_ACT); +    } + +    helper_check_align(env, addr, size - 1); +    addr = asi_address_mask(env, asi, addr); + +    switch (asi) { +    case 0x82: /* Primary no-fault */ +    case 0x8a: /* Primary no-fault LE */ +        if (page_check_range(addr, size, PAGE_READ) == -1) { +#ifdef DEBUG_ASI +            dump_asi("read ", last_addr, asi, size, ret); +#endif +            return 0; +        } +        /* Fall through */ +    case 0x80: /* Primary */ +    case 0x88: /* Primary LE */ +        { +            switch (size) { +            case 1: +                ret = cpu_ldub_data(env, addr); +                break; +            case 2: +                ret = cpu_lduw_data(env, addr); +                break; +            case 4: +                ret = cpu_ldl_data(env, addr); +                break; +            default: +            case 8: +                ret = cpu_ldq_data(env, addr); +                break; +            } +        } +        break; +    case 0x83: /* Secondary no-fault */ +    case 0x8b: /* Secondary no-fault LE */ +        if (page_check_range(addr, size, PAGE_READ) == -1) { +#ifdef DEBUG_ASI +            dump_asi("read ", last_addr, asi, size, ret); +#endif +            return 0; +        } +        /* Fall through */ +    case 0x81: /* Secondary */ +    case 0x89: /* Secondary LE */ +        /* XXX */ +        break; +    default: +        break; +    } + +    /* Convert from little endian */ +    switch (asi) { +    case 0x88: /* Primary LE */ +    case 0x89: /* Secondary LE */ +    case 0x8a: /* Primary no-fault LE */ +    case 0x8b: /* Secondary no-fault LE */ +        switch (size) { +        case 2: +            ret = bswap16(ret); +            break; +        case 4: +            ret = bswap32(ret); +            break; +        case 8: +            ret = bswap64(ret); +            break; +        default: +            break; +        } +    default: +        break; +    } + +    /* Convert to signed number */ +    if (sign) { +        switch (size) { +        case 1: +            ret = (int8_t) ret; +            break; +        case 2: +            ret = (int16_t) ret; +            break; +        case 4: +            ret = (int32_t) ret; +            break; +        default: +            break; +        } +    } +#ifdef DEBUG_ASI +    dump_asi("read ", last_addr, asi, size, ret); +#endif +    return ret; +} + +void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, +                   int asi, int size) +{ +#ifdef DEBUG_ASI +    dump_asi("write", addr, asi, size, val); +#endif +    if (asi < 0x80) { +        helper_raise_exception(env, TT_PRIV_ACT); +    } + +    helper_check_align(env, addr, size - 1); +    addr = asi_address_mask(env, asi, addr); + +    /* Convert to little endian */ +    switch (asi) { +    case 0x88: /* Primary LE */ +    case 0x89: /* Secondary LE */ +        switch (size) { +        case 2: +            val = bswap16(val); +            break; +        case 4: +            val = bswap32(val); +            break; +        case 8: +            val = bswap64(val); +            break; +        default: +            break; +        } +    default: +        break; +    } + +    switch (asi) { +    case 0x80: /* Primary */ +    case 0x88: /* Primary LE */ +        { +            switch (size) { +            case 1: +                cpu_stb_data(env, addr, val); +                break; +            case 2: +                cpu_stw_data(env, addr, val); +                break; +            case 4: +                cpu_stl_data(env, addr, val); +                break; +            case 8: +            default: +                cpu_stq_data(env, addr, val); +                break; +            } +        } +        break; +    case 0x81: /* Secondary */ +    case 0x89: /* Secondary LE */ +        /* XXX */ +        return; + +    case 0x82: /* Primary no-fault, RO */ +    case 0x83: /* Secondary no-fault, RO */ +    case 0x8a: /* Primary no-fault LE, RO */ +    case 0x8b: /* Secondary no-fault LE, RO */ +    default: +        helper_raise_exception(env, TT_DATA_ACCESS); +        return; +    } +} + +#else /* CONFIG_USER_ONLY */ + +uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr, int asi, int size, +                       int sign) +{ +    CPUState *cs = CPU(sparc_env_get_cpu(env)); +    uint64_t ret = 0; +#if defined(DEBUG_ASI) +    target_ulong last_addr = addr; +#endif + +    asi &= 0xff; + +    if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0) +        || (cpu_has_hypervisor(env) +            && asi >= 0x30 && asi < 0x80 +            && !(env->hpstate & HS_PRIV))) { +        helper_raise_exception(env, TT_PRIV_ACT); +    } + +    helper_check_align(env, addr, size - 1); +    addr = asi_address_mask(env, asi, addr); + +    /* process nonfaulting loads first */ +    if ((asi & 0xf6) == 0x82) { +        int mmu_idx; + +        /* secondary space access has lowest asi bit equal to 1 */ +        if (env->pstate & PS_PRIV) { +            mmu_idx = (asi & 1) ? MMU_KERNEL_SECONDARY_IDX : MMU_KERNEL_IDX; +        } else { +            mmu_idx = (asi & 1) ? MMU_USER_SECONDARY_IDX : MMU_USER_IDX; +        } + +        if (cpu_get_phys_page_nofault(env, addr, mmu_idx) == -1ULL) { +#ifdef DEBUG_ASI +            dump_asi("read ", last_addr, asi, size, ret); +#endif +            /* env->exception_index is set in get_physical_address_data(). */ +            helper_raise_exception(env, cs->exception_index); +        } + +        /* convert nonfaulting load ASIs to normal load ASIs */ +        asi &= ~0x02; +    } + +    switch (asi) { +    case 0x10: /* As if user primary */ +    case 0x11: /* As if user secondary */ +    case 0x18: /* As if user primary LE */ +    case 0x19: /* As if user secondary LE */ +    case 0x80: /* Primary */ +    case 0x81: /* Secondary */ +    case 0x88: /* Primary LE */ +    case 0x89: /* Secondary LE */ +    case 0xe2: /* UA2007 Primary block init */ +    case 0xe3: /* UA2007 Secondary block init */ +        if ((asi & 0x80) && (env->pstate & PS_PRIV)) { +            if (cpu_hypervisor_mode(env)) { +                switch (size) { +                case 1: +                    ret = cpu_ldub_hypv(env, addr); +                    break; +                case 2: +                    ret = cpu_lduw_hypv(env, addr); +                    break; +                case 4: +                    ret = cpu_ldl_hypv(env, addr); +                    break; +                default: +                case 8: +                    ret = cpu_ldq_hypv(env, addr); +                    break; +                } +            } else { +                /* secondary space access has lowest asi bit equal to 1 */ +                if (asi & 1) { +                    switch (size) { +                    case 1: +                        ret = cpu_ldub_kernel_secondary(env, addr); +                        break; +                    case 2: +                        ret = cpu_lduw_kernel_secondary(env, addr); +                        break; +                    case 4: +                        ret = cpu_ldl_kernel_secondary(env, addr); +                        break; +                    default: +                    case 8: +                        ret = cpu_ldq_kernel_secondary(env, addr); +                        break; +                    } +                } else { +                    switch (size) { +                    case 1: +                        ret = cpu_ldub_kernel(env, addr); +                        break; +                    case 2: +                        ret = cpu_lduw_kernel(env, addr); +                        break; +                    case 4: +                        ret = cpu_ldl_kernel(env, addr); +                        break; +                    default: +                    case 8: +                        ret = cpu_ldq_kernel(env, addr); +                        break; +                    } +                } +            } +        } else { +            /* secondary space access has lowest asi bit equal to 1 */ +            if (asi & 1) { +                switch (size) { +                case 1: +                    ret = cpu_ldub_user_secondary(env, addr); +                    break; +                case 2: +                    ret = cpu_lduw_user_secondary(env, addr); +                    break; +                case 4: +                    ret = cpu_ldl_user_secondary(env, addr); +                    break; +                default: +                case 8: +                    ret = cpu_ldq_user_secondary(env, addr); +                    break; +                } +            } else { +                switch (size) { +                case 1: +                    ret = cpu_ldub_user(env, addr); +                    break; +                case 2: +                    ret = cpu_lduw_user(env, addr); +                    break; +                case 4: +                    ret = cpu_ldl_user(env, addr); +                    break; +                default: +                case 8: +                    ret = cpu_ldq_user(env, addr); +                    break; +                } +            } +        } +        break; +    case 0x14: /* Bypass */ +    case 0x15: /* Bypass, non-cacheable */ +    case 0x1c: /* Bypass LE */ +    case 0x1d: /* Bypass, non-cacheable LE */ +        { +            switch (size) { +            case 1: +                ret = ldub_phys(cs->as, addr); +                break; +            case 2: +                ret = lduw_phys(cs->as, addr); +                break; +            case 4: +                ret = ldl_phys(cs->as, addr); +                break; +            default: +            case 8: +                ret = ldq_phys(cs->as, addr); +                break; +            } +            break; +        } +    case 0x24: /* Nucleus quad LDD 128 bit atomic */ +    case 0x2c: /* Nucleus quad LDD 128 bit atomic LE +                  Only ldda allowed */ +        helper_raise_exception(env, TT_ILL_INSN); +        return 0; +    case 0x04: /* Nucleus */ +    case 0x0c: /* Nucleus Little Endian (LE) */ +        { +            switch (size) { +            case 1: +                ret = cpu_ldub_nucleus(env, addr); +                break; +            case 2: +                ret = cpu_lduw_nucleus(env, addr); +                break; +            case 4: +                ret = cpu_ldl_nucleus(env, addr); +                break; +            default: +            case 8: +                ret = cpu_ldq_nucleus(env, addr); +                break; +            } +            break; +        } +    case 0x4a: /* UPA config */ +        /* XXX */ +        break; +    case 0x45: /* LSU */ +        ret = env->lsu; +        break; +    case 0x50: /* I-MMU regs */ +        { +            int reg = (addr >> 3) & 0xf; + +            if (reg == 0) { +                /* I-TSB Tag Target register */ +                ret = ultrasparc_tag_target(env->immu.tag_access); +            } else { +                ret = env->immuregs[reg]; +            } + +            break; +        } +    case 0x51: /* I-MMU 8k TSB pointer */ +        { +            /* env->immuregs[5] holds I-MMU TSB register value +               env->immuregs[6] holds I-MMU Tag Access register value */ +            ret = ultrasparc_tsb_pointer(env->immu.tsb, env->immu.tag_access, +                                         8*1024); +            break; +        } +    case 0x52: /* I-MMU 64k TSB pointer */ +        { +            /* env->immuregs[5] holds I-MMU TSB register value +               env->immuregs[6] holds I-MMU Tag Access register value */ +            ret = ultrasparc_tsb_pointer(env->immu.tsb, env->immu.tag_access, +                                         64*1024); +            break; +        } +    case 0x55: /* I-MMU data access */ +        { +            int reg = (addr >> 3) & 0x3f; + +            ret = env->itlb[reg].tte; +            break; +        } +    case 0x56: /* I-MMU tag read */ +        { +            int reg = (addr >> 3) & 0x3f; + +            ret = env->itlb[reg].tag; +            break; +        } +    case 0x58: /* D-MMU regs */ +        { +            int reg = (addr >> 3) & 0xf; + +            if (reg == 0) { +                /* D-TSB Tag Target register */ +                ret = ultrasparc_tag_target(env->dmmu.tag_access); +            } else { +                ret = env->dmmuregs[reg]; +            } +            break; +        } +    case 0x59: /* D-MMU 8k TSB pointer */ +        { +            /* env->dmmuregs[5] holds D-MMU TSB register value +               env->dmmuregs[6] holds D-MMU Tag Access register value */ +            ret = ultrasparc_tsb_pointer(env->dmmu.tsb, env->dmmu.tag_access, +                                         8*1024); +            break; +        } +    case 0x5a: /* D-MMU 64k TSB pointer */ +        { +            /* env->dmmuregs[5] holds D-MMU TSB register value +               env->dmmuregs[6] holds D-MMU Tag Access register value */ +            ret = ultrasparc_tsb_pointer(env->dmmu.tsb, env->dmmu.tag_access, +                                         64*1024); +            break; +        } +    case 0x5d: /* D-MMU data access */ +        { +            int reg = (addr >> 3) & 0x3f; + +            ret = env->dtlb[reg].tte; +            break; +        } +    case 0x5e: /* D-MMU tag read */ +        { +            int reg = (addr >> 3) & 0x3f; + +            ret = env->dtlb[reg].tag; +            break; +        } +    case 0x48: /* Interrupt dispatch, RO */ +        break; +    case 0x49: /* Interrupt data receive */ +        ret = env->ivec_status; +        break; +    case 0x7f: /* Incoming interrupt vector, RO */ +        { +            int reg = (addr >> 4) & 0x3; +            if (reg < 3) { +                ret = env->ivec_data[reg]; +            } +            break; +        } +    case 0x46: /* D-cache data */ +    case 0x47: /* D-cache tag access */ +    case 0x4b: /* E-cache error enable */ +    case 0x4c: /* E-cache asynchronous fault status */ +    case 0x4d: /* E-cache asynchronous fault address */ +    case 0x4e: /* E-cache tag data */ +    case 0x66: /* I-cache instruction access */ +    case 0x67: /* I-cache tag access */ +    case 0x6e: /* I-cache predecode */ +    case 0x6f: /* I-cache LRU etc. */ +    case 0x76: /* E-cache tag */ +    case 0x7e: /* E-cache tag */ +        break; +    case 0x5b: /* D-MMU data pointer */ +    case 0x54: /* I-MMU data in, WO */ +    case 0x57: /* I-MMU demap, WO */ +    case 0x5c: /* D-MMU data in, WO */ +    case 0x5f: /* D-MMU demap, WO */ +    case 0x77: /* Interrupt vector, WO */ +    default: +        cpu_unassigned_access(cs, addr, false, false, 1, size); +        ret = 0; +        break; +    } + +    /* Convert from little endian */ +    switch (asi) { +    case 0x0c: /* Nucleus Little Endian (LE) */ +    case 0x18: /* As if user primary LE */ +    case 0x19: /* As if user secondary LE */ +    case 0x1c: /* Bypass LE */ +    case 0x1d: /* Bypass, non-cacheable LE */ +    case 0x88: /* Primary LE */ +    case 0x89: /* Secondary LE */ +        switch(size) { +        case 2: +            ret = bswap16(ret); +            break; +        case 4: +            ret = bswap32(ret); +            break; +        case 8: +            ret = bswap64(ret); +            break; +        default: +            break; +        } +    default: +        break; +    } + +    /* Convert to signed number */ +    if (sign) { +        switch (size) { +        case 1: +            ret = (int8_t) ret; +            break; +        case 2: +            ret = (int16_t) ret; +            break; +        case 4: +            ret = (int32_t) ret; +            break; +        default: +            break; +        } +    } +#ifdef DEBUG_ASI +    dump_asi("read ", last_addr, asi, size, ret); +#endif +    return ret; +} + +void helper_st_asi(CPUSPARCState *env, target_ulong addr, target_ulong val, +                   int asi, int size) +{ +    SPARCCPU *cpu = sparc_env_get_cpu(env); +    CPUState *cs = CPU(cpu); + +#ifdef DEBUG_ASI +    dump_asi("write", addr, asi, size, val); +#endif + +    asi &= 0xff; + +    if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0) +        || (cpu_has_hypervisor(env) +            && asi >= 0x30 && asi < 0x80 +            && !(env->hpstate & HS_PRIV))) { +        helper_raise_exception(env, TT_PRIV_ACT); +    } + +    helper_check_align(env, addr, size - 1); +    addr = asi_address_mask(env, asi, addr); + +    /* Convert to little endian */ +    switch (asi) { +    case 0x0c: /* Nucleus Little Endian (LE) */ +    case 0x18: /* As if user primary LE */ +    case 0x19: /* As if user secondary LE */ +    case 0x1c: /* Bypass LE */ +    case 0x1d: /* Bypass, non-cacheable LE */ +    case 0x88: /* Primary LE */ +    case 0x89: /* Secondary LE */ +        switch (size) { +        case 2: +            val = bswap16(val); +            break; +        case 4: +            val = bswap32(val); +            break; +        case 8: +            val = bswap64(val); +            break; +        default: +            break; +        } +    default: +        break; +    } + +    switch (asi) { +    case 0x10: /* As if user primary */ +    case 0x11: /* As if user secondary */ +    case 0x18: /* As if user primary LE */ +    case 0x19: /* As if user secondary LE */ +    case 0x80: /* Primary */ +    case 0x81: /* Secondary */ +    case 0x88: /* Primary LE */ +    case 0x89: /* Secondary LE */ +    case 0xe2: /* UA2007 Primary block init */ +    case 0xe3: /* UA2007 Secondary block init */ +        if ((asi & 0x80) && (env->pstate & PS_PRIV)) { +            if (cpu_hypervisor_mode(env)) { +                switch (size) { +                case 1: +                    cpu_stb_hypv(env, addr, val); +                    break; +                case 2: +                    cpu_stw_hypv(env, addr, val); +                    break; +                case 4: +                    cpu_stl_hypv(env, addr, val); +                    break; +                case 8: +                default: +                    cpu_stq_hypv(env, addr, val); +                    break; +                } +            } else { +                /* secondary space access has lowest asi bit equal to 1 */ +                if (asi & 1) { +                    switch (size) { +                    case 1: +                        cpu_stb_kernel_secondary(env, addr, val); +                        break; +                    case 2: +                        cpu_stw_kernel_secondary(env, addr, val); +                        break; +                    case 4: +                        cpu_stl_kernel_secondary(env, addr, val); +                        break; +                    case 8: +                    default: +                        cpu_stq_kernel_secondary(env, addr, val); +                        break; +                    } +                } else { +                    switch (size) { +                    case 1: +                        cpu_stb_kernel(env, addr, val); +                        break; +                    case 2: +                        cpu_stw_kernel(env, addr, val); +                        break; +                    case 4: +                        cpu_stl_kernel(env, addr, val); +                        break; +                    case 8: +                    default: +                        cpu_stq_kernel(env, addr, val); +                        break; +                    } +                } +            } +        } else { +            /* secondary space access has lowest asi bit equal to 1 */ +            if (asi & 1) { +                switch (size) { +                case 1: +                    cpu_stb_user_secondary(env, addr, val); +                    break; +                case 2: +                    cpu_stw_user_secondary(env, addr, val); +                    break; +                case 4: +                    cpu_stl_user_secondary(env, addr, val); +                    break; +                case 8: +                default: +                    cpu_stq_user_secondary(env, addr, val); +                    break; +                } +            } else { +                switch (size) { +                case 1: +                    cpu_stb_user(env, addr, val); +                    break; +                case 2: +                    cpu_stw_user(env, addr, val); +                    break; +                case 4: +                    cpu_stl_user(env, addr, val); +                    break; +                case 8: +                default: +                    cpu_stq_user(env, addr, val); +                    break; +                } +            } +        } +        break; +    case 0x14: /* Bypass */ +    case 0x15: /* Bypass, non-cacheable */ +    case 0x1c: /* Bypass LE */ +    case 0x1d: /* Bypass, non-cacheable LE */ +        { +            switch (size) { +            case 1: +                stb_phys(cs->as, addr, val); +                break; +            case 2: +                stw_phys(cs->as, addr, val); +                break; +            case 4: +                stl_phys(cs->as, addr, val); +                break; +            case 8: +            default: +                stq_phys(cs->as, addr, val); +                break; +            } +        } +        return; +    case 0x24: /* Nucleus quad LDD 128 bit atomic */ +    case 0x2c: /* Nucleus quad LDD 128 bit atomic LE +                  Only ldda allowed */ +        helper_raise_exception(env, TT_ILL_INSN); +        return; +    case 0x04: /* Nucleus */ +    case 0x0c: /* Nucleus Little Endian (LE) */ +        { +            switch (size) { +            case 1: +                cpu_stb_nucleus(env, addr, val); +                break; +            case 2: +                cpu_stw_nucleus(env, addr, val); +                break; +            case 4: +                cpu_stl_nucleus(env, addr, val); +                break; +            default: +            case 8: +                cpu_stq_nucleus(env, addr, val); +                break; +            } +            break; +        } + +    case 0x4a: /* UPA config */ +        /* XXX */ +        return; +    case 0x45: /* LSU */ +        { +            uint64_t oldreg; + +            oldreg = env->lsu; +            env->lsu = val & (DMMU_E | IMMU_E); +            /* Mappings generated during D/I MMU disabled mode are +               invalid in normal mode */ +            if (oldreg != env->lsu) { +                DPRINTF_MMU("LSU change: 0x%" PRIx64 " -> 0x%" PRIx64 "\n", +                            oldreg, env->lsu); +#ifdef DEBUG_MMU +                dump_mmu(stdout, fprintf, env); +#endif +                tlb_flush(CPU(cpu), 1); +            } +            return; +        } +    case 0x50: /* I-MMU regs */ +        { +            int reg = (addr >> 3) & 0xf; +            uint64_t oldreg; + +            oldreg = env->immuregs[reg]; +            switch (reg) { +            case 0: /* RO */ +                return; +            case 1: /* Not in I-MMU */ +            case 2: +                return; +            case 3: /* SFSR */ +                if ((val & 1) == 0) { +                    val = 0; /* Clear SFSR */ +                } +                env->immu.sfsr = val; +                break; +            case 4: /* RO */ +                return; +            case 5: /* TSB access */ +                DPRINTF_MMU("immu TSB write: 0x%016" PRIx64 " -> 0x%016" +                            PRIx64 "\n", env->immu.tsb, val); +                env->immu.tsb = val; +                break; +            case 6: /* Tag access */ +                env->immu.tag_access = val; +                break; +            case 7: +            case 8: +                return; +            default: +                break; +            } + +            if (oldreg != env->immuregs[reg]) { +                DPRINTF_MMU("immu change reg[%d]: 0x%016" PRIx64 " -> 0x%016" +                            PRIx64 "\n", reg, oldreg, env->immuregs[reg]); +            } +#ifdef DEBUG_MMU +            dump_mmu(stdout, fprintf, env); +#endif +            return; +        } +    case 0x54: /* I-MMU data in */ +        replace_tlb_1bit_lru(env->itlb, env->immu.tag_access, val, "immu", env); +        return; +    case 0x55: /* I-MMU data access */ +        { +            /* TODO: auto demap */ + +            unsigned int i = (addr >> 3) & 0x3f; + +            replace_tlb_entry(&env->itlb[i], env->immu.tag_access, val, env); + +#ifdef DEBUG_MMU +            DPRINTF_MMU("immu data access replaced entry [%i]\n", i); +            dump_mmu(stdout, fprintf, env); +#endif +            return; +        } +    case 0x57: /* I-MMU demap */ +        demap_tlb(env->itlb, addr, "immu", env); +        return; +    case 0x58: /* D-MMU regs */ +        { +            int reg = (addr >> 3) & 0xf; +            uint64_t oldreg; + +            oldreg = env->dmmuregs[reg]; +            switch (reg) { +            case 0: /* RO */ +            case 4: +                return; +            case 3: /* SFSR */ +                if ((val & 1) == 0) { +                    val = 0; /* Clear SFSR, Fault address */ +                    env->dmmu.sfar = 0; +                } +                env->dmmu.sfsr = val; +                break; +            case 1: /* Primary context */ +                env->dmmu.mmu_primary_context = val; +                /* can be optimized to only flush MMU_USER_IDX +                   and MMU_KERNEL_IDX entries */ +                tlb_flush(CPU(cpu), 1); +                break; +            case 2: /* Secondary context */ +                env->dmmu.mmu_secondary_context = val; +                /* can be optimized to only flush MMU_USER_SECONDARY_IDX +                   and MMU_KERNEL_SECONDARY_IDX entries */ +                tlb_flush(CPU(cpu), 1); +                break; +            case 5: /* TSB access */ +                DPRINTF_MMU("dmmu TSB write: 0x%016" PRIx64 " -> 0x%016" +                            PRIx64 "\n", env->dmmu.tsb, val); +                env->dmmu.tsb = val; +                break; +            case 6: /* Tag access */ +                env->dmmu.tag_access = val; +                break; +            case 7: /* Virtual Watchpoint */ +            case 8: /* Physical Watchpoint */ +            default: +                env->dmmuregs[reg] = val; +                break; +            } + +            if (oldreg != env->dmmuregs[reg]) { +                DPRINTF_MMU("dmmu change reg[%d]: 0x%016" PRIx64 " -> 0x%016" +                            PRIx64 "\n", reg, oldreg, env->dmmuregs[reg]); +            } +#ifdef DEBUG_MMU +            dump_mmu(stdout, fprintf, env); +#endif +            return; +        } +    case 0x5c: /* D-MMU data in */ +        replace_tlb_1bit_lru(env->dtlb, env->dmmu.tag_access, val, "dmmu", env); +        return; +    case 0x5d: /* D-MMU data access */ +        { +            unsigned int i = (addr >> 3) & 0x3f; + +            replace_tlb_entry(&env->dtlb[i], env->dmmu.tag_access, val, env); + +#ifdef DEBUG_MMU +            DPRINTF_MMU("dmmu data access replaced entry [%i]\n", i); +            dump_mmu(stdout, fprintf, env); +#endif +            return; +        } +    case 0x5f: /* D-MMU demap */ +        demap_tlb(env->dtlb, addr, "dmmu", env); +        return; +    case 0x49: /* Interrupt data receive */ +        env->ivec_status = val & 0x20; +        return; +    case 0x46: /* D-cache data */ +    case 0x47: /* D-cache tag access */ +    case 0x4b: /* E-cache error enable */ +    case 0x4c: /* E-cache asynchronous fault status */ +    case 0x4d: /* E-cache asynchronous fault address */ +    case 0x4e: /* E-cache tag data */ +    case 0x66: /* I-cache instruction access */ +    case 0x67: /* I-cache tag access */ +    case 0x6e: /* I-cache predecode */ +    case 0x6f: /* I-cache LRU etc. */ +    case 0x76: /* E-cache tag */ +    case 0x7e: /* E-cache tag */ +        return; +    case 0x51: /* I-MMU 8k TSB pointer, RO */ +    case 0x52: /* I-MMU 64k TSB pointer, RO */ +    case 0x56: /* I-MMU tag read, RO */ +    case 0x59: /* D-MMU 8k TSB pointer, RO */ +    case 0x5a: /* D-MMU 64k TSB pointer, RO */ +    case 0x5b: /* D-MMU data pointer, RO */ +    case 0x5e: /* D-MMU tag read, RO */ +    case 0x48: /* Interrupt dispatch, RO */ +    case 0x7f: /* Incoming interrupt vector, RO */ +    case 0x82: /* Primary no-fault, RO */ +    case 0x83: /* Secondary no-fault, RO */ +    case 0x8a: /* Primary no-fault LE, RO */ +    case 0x8b: /* Secondary no-fault LE, RO */ +    default: +        cpu_unassigned_access(cs, addr, true, false, 1, size); +        return; +    } +} +#endif /* CONFIG_USER_ONLY */ + +void helper_ldda_asi(CPUSPARCState *env, target_ulong addr, int asi, int rd) +{ +    if ((asi < 0x80 && (env->pstate & PS_PRIV) == 0) +        || (cpu_has_hypervisor(env) +            && asi >= 0x30 && asi < 0x80 +            && !(env->hpstate & HS_PRIV))) { +        helper_raise_exception(env, TT_PRIV_ACT); +    } + +    addr = asi_address_mask(env, asi, addr); + +    switch (asi) { +#if !defined(CONFIG_USER_ONLY) +    case 0x24: /* Nucleus quad LDD 128 bit atomic */ +    case 0x2c: /* Nucleus quad LDD 128 bit atomic LE */ +        helper_check_align(env, addr, 0xf); +        if (rd == 0) { +            env->gregs[1] = cpu_ldq_nucleus(env, addr + 8); +            if (asi == 0x2c) { +                bswap64s(&env->gregs[1]); +            } +        } else if (rd < 8) { +            env->gregs[rd] = cpu_ldq_nucleus(env, addr); +            env->gregs[rd + 1] = cpu_ldq_nucleus(env, addr + 8); +            if (asi == 0x2c) { +                bswap64s(&env->gregs[rd]); +                bswap64s(&env->gregs[rd + 1]); +            } +        } else { +            env->regwptr[rd] = cpu_ldq_nucleus(env, addr); +            env->regwptr[rd + 1] = cpu_ldq_nucleus(env, addr + 8); +            if (asi == 0x2c) { +                bswap64s(&env->regwptr[rd]); +                bswap64s(&env->regwptr[rd + 1]); +            } +        } +        break; +#endif +    default: +        helper_check_align(env, addr, 0x3); +        if (rd == 0) { +            env->gregs[1] = helper_ld_asi(env, addr + 4, asi, 4, 0); +        } else if (rd < 8) { +            env->gregs[rd] = helper_ld_asi(env, addr, asi, 4, 0); +            env->gregs[rd + 1] = helper_ld_asi(env, addr + 4, asi, 4, 0); +        } else { +            env->regwptr[rd] = helper_ld_asi(env, addr, asi, 4, 0); +            env->regwptr[rd + 1] = helper_ld_asi(env, addr + 4, asi, 4, 0); +        } +        break; +    } +} + +void helper_ldf_asi(CPUSPARCState *env, target_ulong addr, int asi, int size, +                    int rd) +{ +    unsigned int i; +    target_ulong val; + +    helper_check_align(env, addr, 3); +    addr = asi_address_mask(env, asi, addr); + +    switch (asi) { +    case 0xf0: /* UA2007/JPS1 Block load primary */ +    case 0xf1: /* UA2007/JPS1 Block load secondary */ +    case 0xf8: /* UA2007/JPS1 Block load primary LE */ +    case 0xf9: /* UA2007/JPS1 Block load secondary LE */ +        if (rd & 7) { +            helper_raise_exception(env, TT_ILL_INSN); +            return; +        } +        helper_check_align(env, addr, 0x3f); +        for (i = 0; i < 8; i++, rd += 2, addr += 8) { +            env->fpr[rd / 2].ll = helper_ld_asi(env, addr, asi & 0x8f, 8, 0); +        } +        return; + +    case 0x16: /* UA2007 Block load primary, user privilege */ +    case 0x17: /* UA2007 Block load secondary, user privilege */ +    case 0x1e: /* UA2007 Block load primary LE, user privilege */ +    case 0x1f: /* UA2007 Block load secondary LE, user privilege */ +    case 0x70: /* JPS1 Block load primary, user privilege */ +    case 0x71: /* JPS1 Block load secondary, user privilege */ +    case 0x78: /* JPS1 Block load primary LE, user privilege */ +    case 0x79: /* JPS1 Block load secondary LE, user privilege */ +        if (rd & 7) { +            helper_raise_exception(env, TT_ILL_INSN); +            return; +        } +        helper_check_align(env, addr, 0x3f); +        for (i = 0; i < 8; i++, rd += 2, addr += 8) { +            env->fpr[rd / 2].ll = helper_ld_asi(env, addr, asi & 0x19, 8, 0); +        } +        return; + +    default: +        break; +    } + +    switch (size) { +    default: +    case 4: +        val = helper_ld_asi(env, addr, asi, size, 0); +        if (rd & 1) { +            env->fpr[rd / 2].l.lower = val; +        } else { +            env->fpr[rd / 2].l.upper = val; +        } +        break; +    case 8: +        env->fpr[rd / 2].ll = helper_ld_asi(env, addr, asi, size, 0); +        break; +    case 16: +        env->fpr[rd / 2].ll = helper_ld_asi(env, addr, asi, 8, 0); +        env->fpr[rd / 2 + 1].ll = helper_ld_asi(env, addr + 8, asi, 8, 0); +        break; +    } +} + +void helper_stf_asi(CPUSPARCState *env, target_ulong addr, int asi, int size, +                    int rd) +{ +    unsigned int i; +    target_ulong val; + +    addr = asi_address_mask(env, asi, addr); + +    switch (asi) { +    case 0xe0: /* UA2007/JPS1 Block commit store primary (cache flush) */ +    case 0xe1: /* UA2007/JPS1 Block commit store secondary (cache flush) */ +    case 0xf0: /* UA2007/JPS1 Block store primary */ +    case 0xf1: /* UA2007/JPS1 Block store secondary */ +    case 0xf8: /* UA2007/JPS1 Block store primary LE */ +    case 0xf9: /* UA2007/JPS1 Block store secondary LE */ +        if (rd & 7) { +            helper_raise_exception(env, TT_ILL_INSN); +            return; +        } +        helper_check_align(env, addr, 0x3f); +        for (i = 0; i < 8; i++, rd += 2, addr += 8) { +            helper_st_asi(env, addr, env->fpr[rd / 2].ll, asi & 0x8f, 8); +        } + +        return; +    case 0x16: /* UA2007 Block load primary, user privilege */ +    case 0x17: /* UA2007 Block load secondary, user privilege */ +    case 0x1e: /* UA2007 Block load primary LE, user privilege */ +    case 0x1f: /* UA2007 Block load secondary LE, user privilege */ +    case 0x70: /* JPS1 Block store primary, user privilege */ +    case 0x71: /* JPS1 Block store secondary, user privilege */ +    case 0x78: /* JPS1 Block load primary LE, user privilege */ +    case 0x79: /* JPS1 Block load secondary LE, user privilege */ +        if (rd & 7) { +            helper_raise_exception(env, TT_ILL_INSN); +            return; +        } +        helper_check_align(env, addr, 0x3f); +        for (i = 0; i < 8; i++, rd += 2, addr += 8) { +            helper_st_asi(env, addr, env->fpr[rd / 2].ll, asi & 0x19, 8); +        } + +        return; +    case 0xd2: /* 16-bit floating point load primary */ +    case 0xd3: /* 16-bit floating point load secondary */ +    case 0xda: /* 16-bit floating point load primary, LE */ +    case 0xdb: /* 16-bit floating point load secondary, LE */ +        helper_check_align(env, addr, 1); +        /* Fall through */ +    case 0xd0: /* 8-bit floating point load primary */ +    case 0xd1: /* 8-bit floating point load secondary */ +    case 0xd8: /* 8-bit floating point load primary, LE */ +    case 0xd9: /* 8-bit floating point load secondary, LE */ +        val = env->fpr[rd / 2].l.lower; +        helper_st_asi(env, addr, val, asi & 0x8d, ((asi & 2) >> 1) + 1); +        return; +    default: +        helper_check_align(env, addr, 3); +        break; +    } + +    switch (size) { +    default: +    case 4: +        if (rd & 1) { +            val = env->fpr[rd / 2].l.lower; +        } else { +            val = env->fpr[rd / 2].l.upper; +        } +        helper_st_asi(env, addr, val, asi, size); +        break; +    case 8: +        helper_st_asi(env, addr, env->fpr[rd / 2].ll, asi, size); +        break; +    case 16: +        helper_st_asi(env, addr, env->fpr[rd / 2].ll, asi, 8); +        helper_st_asi(env, addr + 8, env->fpr[rd / 2 + 1].ll, asi, 8); +        break; +    } +} + +target_ulong helper_casx_asi(CPUSPARCState *env, target_ulong addr, +                             target_ulong val1, target_ulong val2, +                             uint32_t asi) +{ +    target_ulong ret; + +    ret = helper_ld_asi(env, addr, asi, 8, 0); +    if (val2 == ret) { +        helper_st_asi(env, addr, val1, asi, 8); +    } +    return ret; +} +#endif /* TARGET_SPARC64 */ + +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) +target_ulong helper_cas_asi(CPUSPARCState *env, target_ulong addr, +                            target_ulong val1, target_ulong val2, uint32_t asi) +{ +    target_ulong ret; + +    val2 &= 0xffffffffUL; +    ret = helper_ld_asi(env, addr, asi, 4, 0); +    ret &= 0xffffffffUL; +    if (val2 == ret) { +        helper_st_asi(env, addr, val1 & 0xffffffffUL, asi, 4); +    } +    return ret; +} +#endif /* !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) */ + +void helper_ldqf(CPUSPARCState *env, target_ulong addr, int mem_idx) +{ +    /* XXX add 128 bit load */ +    CPU_QuadU u; + +    helper_check_align(env, addr, 7); +#if !defined(CONFIG_USER_ONLY) +    switch (mem_idx) { +    case MMU_USER_IDX: +        u.ll.upper = cpu_ldq_user(env, addr); +        u.ll.lower = cpu_ldq_user(env, addr + 8); +        QT0 = u.q; +        break; +    case MMU_KERNEL_IDX: +        u.ll.upper = cpu_ldq_kernel(env, addr); +        u.ll.lower = cpu_ldq_kernel(env, addr + 8); +        QT0 = u.q; +        break; +#ifdef TARGET_SPARC64 +    case MMU_HYPV_IDX: +        u.ll.upper = cpu_ldq_hypv(env, addr); +        u.ll.lower = cpu_ldq_hypv(env, addr + 8); +        QT0 = u.q; +        break; +#endif +    default: +        DPRINTF_MMU("helper_ldqf: need to check MMU idx %d\n", mem_idx); +        break; +    } +#else +    u.ll.upper = cpu_ldq_data(env, address_mask(env, addr)); +    u.ll.lower = cpu_ldq_data(env, address_mask(env, addr + 8)); +    QT0 = u.q; +#endif +} + +void helper_stqf(CPUSPARCState *env, target_ulong addr, int mem_idx) +{ +    /* XXX add 128 bit store */ +    CPU_QuadU u; + +    helper_check_align(env, addr, 7); +#if !defined(CONFIG_USER_ONLY) +    switch (mem_idx) { +    case MMU_USER_IDX: +        u.q = QT0; +        cpu_stq_user(env, addr, u.ll.upper); +        cpu_stq_user(env, addr + 8, u.ll.lower); +        break; +    case MMU_KERNEL_IDX: +        u.q = QT0; +        cpu_stq_kernel(env, addr, u.ll.upper); +        cpu_stq_kernel(env, addr + 8, u.ll.lower); +        break; +#ifdef TARGET_SPARC64 +    case MMU_HYPV_IDX: +        u.q = QT0; +        cpu_stq_hypv(env, addr, u.ll.upper); +        cpu_stq_hypv(env, addr + 8, u.ll.lower); +        break; +#endif +    default: +        DPRINTF_MMU("helper_stqf: need to check MMU idx %d\n", mem_idx); +        break; +    } +#else +    u.q = QT0; +    cpu_stq_data(env, address_mask(env, addr), u.ll.upper); +    cpu_stq_data(env, address_mask(env, addr + 8), u.ll.lower); +#endif +} + +#if !defined(CONFIG_USER_ONLY) +#ifndef TARGET_SPARC64 +void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr, +                                 bool is_write, bool is_exec, int is_asi, +                                 unsigned size) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; +    int fault_type; + +#ifdef DEBUG_UNASSIGNED +    if (is_asi) { +        printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx +               " asi 0x%02x from " TARGET_FMT_lx "\n", +               is_exec ? "exec" : is_write ? "write" : "read", size, +               size == 1 ? "" : "s", addr, is_asi, env->pc); +    } else { +        printf("Unassigned mem %s access of %d byte%s to " TARGET_FMT_plx +               " from " TARGET_FMT_lx "\n", +               is_exec ? "exec" : is_write ? "write" : "read", size, +               size == 1 ? "" : "s", addr, env->pc); +    } +#endif +    /* Don't overwrite translation and access faults */ +    fault_type = (env->mmuregs[3] & 0x1c) >> 2; +    if ((fault_type > 4) || (fault_type == 0)) { +        env->mmuregs[3] = 0; /* Fault status register */ +        if (is_asi) { +            env->mmuregs[3] |= 1 << 16; +        } +        if (env->psrs) { +            env->mmuregs[3] |= 1 << 5; +        } +        if (is_exec) { +            env->mmuregs[3] |= 1 << 6; +        } +        if (is_write) { +            env->mmuregs[3] |= 1 << 7; +        } +        env->mmuregs[3] |= (5 << 2) | 2; +        /* SuperSPARC will never place instruction fault addresses in the FAR */ +        if (!is_exec) { +            env->mmuregs[4] = addr; /* Fault address register */ +        } +    } +    /* overflow (same type fault was not read before another fault) */ +    if (fault_type == ((env->mmuregs[3] & 0x1c)) >> 2) { +        env->mmuregs[3] |= 1; +    } + +    if ((env->mmuregs[0] & MMU_E) && !(env->mmuregs[0] & MMU_NF)) { +        if (is_exec) { +            helper_raise_exception(env, TT_CODE_ACCESS); +        } else { +            helper_raise_exception(env, TT_DATA_ACCESS); +        } +    } + +    /* flush neverland mappings created during no-fault mode, +       so the sequential MMU faults report proper fault types */ +    if (env->mmuregs[0] & MMU_NF) { +        tlb_flush(cs, 1); +    } +} +#else +void sparc_cpu_unassigned_access(CPUState *cs, hwaddr addr, +                                 bool is_write, bool is_exec, int is_asi, +                                 unsigned size) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; + +#ifdef DEBUG_UNASSIGNED +    printf("Unassigned mem access to " TARGET_FMT_plx " from " TARGET_FMT_lx +           "\n", addr, env->pc); +#endif + +    if (is_exec) { +        helper_raise_exception(env, TT_CODE_ACCESS); +    } else { +        helper_raise_exception(env, TT_DATA_ACCESS); +    } +} +#endif +#endif + +#if !defined(CONFIG_USER_ONLY) +void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cs, +                                                 vaddr addr, int is_write, +                                                 int is_user, uintptr_t retaddr) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; + +#ifdef DEBUG_UNALIGNED +    printf("Unaligned access to 0x" TARGET_FMT_lx " from 0x" TARGET_FMT_lx +           "\n", addr, env->pc); +#endif +    if (retaddr) { +        cpu_restore_state(CPU(cpu), retaddr); +    } +    helper_raise_exception(env, TT_UNALIGNED); +} + +/* try to fill the TLB and return an exception if error. If retaddr is +   NULL, it means that the function was called in C code (i.e. not +   from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, +              uintptr_t retaddr) +{ +    int ret; + +    ret = sparc_cpu_handle_mmu_fault(cs, addr, is_write, mmu_idx); +    if (ret) { +        if (retaddr) { +            cpu_restore_state(cs, retaddr); +        } +        cpu_loop_exit(cs); +    } +} +#endif diff --git a/target-sparc/machine.c b/target-sparc/machine.c new file mode 100644 index 00000000..3f3de4c6 --- /dev/null +++ b/target-sparc/machine.c @@ -0,0 +1,218 @@ +#include "hw/hw.h" +#include "hw/boards.h" +#include "qemu/timer.h" + +#include "cpu.h" + +void cpu_save(QEMUFile *f, void *opaque) +{ +    CPUSPARCState *env = opaque; +    int i; +    uint32_t tmp; + +    // if env->cwp == env->nwindows - 1, this will set the ins of the last +    // window as the outs of the first window +    cpu_set_cwp(env, env->cwp); + +    for(i = 0; i < 8; i++) +        qemu_put_betls(f, &env->gregs[i]); +    qemu_put_be32s(f, &env->nwindows); +    for(i = 0; i < env->nwindows * 16; i++) +        qemu_put_betls(f, &env->regbase[i]); + +    /* FPU */ +    for (i = 0; i < TARGET_DPREGS; i++) { +        qemu_put_be32(f, env->fpr[i].l.upper); +        qemu_put_be32(f, env->fpr[i].l.lower); +    } + +    qemu_put_betls(f, &env->pc); +    qemu_put_betls(f, &env->npc); +    qemu_put_betls(f, &env->y); +    tmp = cpu_get_psr(env); +    qemu_put_be32(f, tmp); +    qemu_put_betls(f, &env->fsr); +    qemu_put_betls(f, &env->tbr); +    tmp = env->interrupt_index; +    qemu_put_be32(f, tmp); +    qemu_put_be32s(f, &env->pil_in); +#ifndef TARGET_SPARC64 +    qemu_put_be32s(f, &env->wim); +    /* MMU */ +    for (i = 0; i < 32; i++) +        qemu_put_be32s(f, &env->mmuregs[i]); +    for (i = 0; i < 4; i++) { +        qemu_put_be64s(f, &env->mxccdata[i]); +    } +    for (i = 0; i < 8; i++) { +        qemu_put_be64s(f, &env->mxccregs[i]); +    } +    qemu_put_be32s(f, &env->mmubpctrv); +    qemu_put_be32s(f, &env->mmubpctrc); +    qemu_put_be32s(f, &env->mmubpctrs); +    qemu_put_be64s(f, &env->mmubpaction); +    for (i = 0; i < 4; i++) { +        qemu_put_be64s(f, &env->mmubpregs[i]); +    } +#else +    qemu_put_be64s(f, &env->lsu); +    for (i = 0; i < 16; i++) { +        qemu_put_be64s(f, &env->immuregs[i]); +        qemu_put_be64s(f, &env->dmmuregs[i]); +    } +    for (i = 0; i < 64; i++) { +        qemu_put_be64s(f, &env->itlb[i].tag); +        qemu_put_be64s(f, &env->itlb[i].tte); +        qemu_put_be64s(f, &env->dtlb[i].tag); +        qemu_put_be64s(f, &env->dtlb[i].tte); +    } +    qemu_put_be32s(f, &env->mmu_version); +    for (i = 0; i < MAXTL_MAX; i++) { +        qemu_put_be64s(f, &env->ts[i].tpc); +        qemu_put_be64s(f, &env->ts[i].tnpc); +        qemu_put_be64s(f, &env->ts[i].tstate); +        qemu_put_be32s(f, &env->ts[i].tt); +    } +    qemu_put_be32s(f, &env->xcc); +    qemu_put_be32s(f, &env->asi); +    qemu_put_be32s(f, &env->pstate); +    qemu_put_be32s(f, &env->tl); +    qemu_put_be32s(f, &env->cansave); +    qemu_put_be32s(f, &env->canrestore); +    qemu_put_be32s(f, &env->otherwin); +    qemu_put_be32s(f, &env->wstate); +    qemu_put_be32s(f, &env->cleanwin); +    for (i = 0; i < 8; i++) +        qemu_put_be64s(f, &env->agregs[i]); +    for (i = 0; i < 8; i++) +        qemu_put_be64s(f, &env->bgregs[i]); +    for (i = 0; i < 8; i++) +        qemu_put_be64s(f, &env->igregs[i]); +    for (i = 0; i < 8; i++) +        qemu_put_be64s(f, &env->mgregs[i]); +    qemu_put_be64s(f, &env->fprs); +    qemu_put_be64s(f, &env->tick_cmpr); +    qemu_put_be64s(f, &env->stick_cmpr); +    cpu_put_timer(f, env->tick); +    cpu_put_timer(f, env->stick); +    qemu_put_be64s(f, &env->gsr); +    qemu_put_be32s(f, &env->gl); +    qemu_put_be64s(f, &env->hpstate); +    for (i = 0; i < MAXTL_MAX; i++) +        qemu_put_be64s(f, &env->htstate[i]); +    qemu_put_be64s(f, &env->hintp); +    qemu_put_be64s(f, &env->htba); +    qemu_put_be64s(f, &env->hver); +    qemu_put_be64s(f, &env->hstick_cmpr); +    qemu_put_be64s(f, &env->ssr); +    cpu_put_timer(f, env->hstick); +#endif +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ +    CPUSPARCState *env = opaque; +    SPARCCPU *cpu = sparc_env_get_cpu(env); +    int i; +    uint32_t tmp; + +    if (version_id < 6) +        return -EINVAL; +    for(i = 0; i < 8; i++) +        qemu_get_betls(f, &env->gregs[i]); +    qemu_get_be32s(f, &env->nwindows); +    for(i = 0; i < env->nwindows * 16; i++) +        qemu_get_betls(f, &env->regbase[i]); + +    /* FPU */ +    for (i = 0; i < TARGET_DPREGS; i++) { +        env->fpr[i].l.upper = qemu_get_be32(f); +        env->fpr[i].l.lower = qemu_get_be32(f); +    } + +    qemu_get_betls(f, &env->pc); +    qemu_get_betls(f, &env->npc); +    qemu_get_betls(f, &env->y); +    tmp = qemu_get_be32(f); +    env->cwp = 0; /* needed to ensure that the wrapping registers are +                     correctly updated */ +    cpu_put_psr(env, tmp); +    qemu_get_betls(f, &env->fsr); +    qemu_get_betls(f, &env->tbr); +    tmp = qemu_get_be32(f); +    env->interrupt_index = tmp; +    qemu_get_be32s(f, &env->pil_in); +#ifndef TARGET_SPARC64 +    qemu_get_be32s(f, &env->wim); +    /* MMU */ +    for (i = 0; i < 32; i++) +        qemu_get_be32s(f, &env->mmuregs[i]); +    for (i = 0; i < 4; i++) { +        qemu_get_be64s(f, &env->mxccdata[i]); +    } +    for (i = 0; i < 8; i++) { +        qemu_get_be64s(f, &env->mxccregs[i]); +    } +    qemu_get_be32s(f, &env->mmubpctrv); +    qemu_get_be32s(f, &env->mmubpctrc); +    qemu_get_be32s(f, &env->mmubpctrs); +    qemu_get_be64s(f, &env->mmubpaction); +    for (i = 0; i < 4; i++) { +        qemu_get_be64s(f, &env->mmubpregs[i]); +    } +#else +    qemu_get_be64s(f, &env->lsu); +    for (i = 0; i < 16; i++) { +        qemu_get_be64s(f, &env->immuregs[i]); +        qemu_get_be64s(f, &env->dmmuregs[i]); +    } +    for (i = 0; i < 64; i++) { +        qemu_get_be64s(f, &env->itlb[i].tag); +        qemu_get_be64s(f, &env->itlb[i].tte); +        qemu_get_be64s(f, &env->dtlb[i].tag); +        qemu_get_be64s(f, &env->dtlb[i].tte); +    } +    qemu_get_be32s(f, &env->mmu_version); +    for (i = 0; i < MAXTL_MAX; i++) { +        qemu_get_be64s(f, &env->ts[i].tpc); +        qemu_get_be64s(f, &env->ts[i].tnpc); +        qemu_get_be64s(f, &env->ts[i].tstate); +        qemu_get_be32s(f, &env->ts[i].tt); +    } +    qemu_get_be32s(f, &env->xcc); +    qemu_get_be32s(f, &env->asi); +    qemu_get_be32s(f, &env->pstate); +    qemu_get_be32s(f, &env->tl); +    qemu_get_be32s(f, &env->cansave); +    qemu_get_be32s(f, &env->canrestore); +    qemu_get_be32s(f, &env->otherwin); +    qemu_get_be32s(f, &env->wstate); +    qemu_get_be32s(f, &env->cleanwin); +    for (i = 0; i < 8; i++) +        qemu_get_be64s(f, &env->agregs[i]); +    for (i = 0; i < 8; i++) +        qemu_get_be64s(f, &env->bgregs[i]); +    for (i = 0; i < 8; i++) +        qemu_get_be64s(f, &env->igregs[i]); +    for (i = 0; i < 8; i++) +        qemu_get_be64s(f, &env->mgregs[i]); +    qemu_get_be64s(f, &env->fprs); +    qemu_get_be64s(f, &env->tick_cmpr); +    qemu_get_be64s(f, &env->stick_cmpr); +    cpu_get_timer(f, env->tick); +    cpu_get_timer(f, env->stick); +    qemu_get_be64s(f, &env->gsr); +    qemu_get_be32s(f, &env->gl); +    qemu_get_be64s(f, &env->hpstate); +    for (i = 0; i < MAXTL_MAX; i++) +        qemu_get_be64s(f, &env->htstate[i]); +    qemu_get_be64s(f, &env->hintp); +    qemu_get_be64s(f, &env->htba); +    qemu_get_be64s(f, &env->hver); +    qemu_get_be64s(f, &env->hstick_cmpr); +    qemu_get_be64s(f, &env->ssr); +    cpu_get_timer(f, env->hstick); +#endif +    tlb_flush(CPU(cpu), 1); +    return 0; +} diff --git a/target-sparc/mmu_helper.c b/target-sparc/mmu_helper.c new file mode 100644 index 00000000..2a0c6f0d --- /dev/null +++ b/target-sparc/mmu_helper.c @@ -0,0 +1,867 @@ +/* + *  Sparc MMU helpers + * + *  Copyright (c) 2003-2005 Fabrice Bellard + * + * 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 "trace.h" +#include "exec/address-spaces.h" + +/* Sparc MMU emulation */ + +#if defined(CONFIG_USER_ONLY) + +int sparc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, +                               int mmu_idx) +{ +    if (rw & 2) { +        cs->exception_index = TT_TFAULT; +    } else { +        cs->exception_index = TT_DFAULT; +    } +    return 1; +} + +#else + +#ifndef TARGET_SPARC64 +/* + * Sparc V8 Reference MMU (SRMMU) + */ +static const int access_table[8][8] = { +    { 0, 0, 0, 0, 8, 0, 12, 12 }, +    { 0, 0, 0, 0, 8, 0, 0, 0 }, +    { 8, 8, 0, 0, 0, 8, 12, 12 }, +    { 8, 8, 0, 0, 0, 8, 0, 0 }, +    { 8, 0, 8, 0, 8, 8, 12, 12 }, +    { 8, 0, 8, 0, 8, 0, 8, 0 }, +    { 8, 8, 8, 0, 8, 8, 12, 12 }, +    { 8, 8, 8, 0, 8, 8, 8, 0 } +}; + +static const int perm_table[2][8] = { +    { +        PAGE_READ, +        PAGE_READ | PAGE_WRITE, +        PAGE_READ | PAGE_EXEC, +        PAGE_READ | PAGE_WRITE | PAGE_EXEC, +        PAGE_EXEC, +        PAGE_READ | PAGE_WRITE, +        PAGE_READ | PAGE_EXEC, +        PAGE_READ | PAGE_WRITE | PAGE_EXEC +    }, +    { +        PAGE_READ, +        PAGE_READ | PAGE_WRITE, +        PAGE_READ | PAGE_EXEC, +        PAGE_READ | PAGE_WRITE | PAGE_EXEC, +        PAGE_EXEC, +        PAGE_READ, +        0, +        0, +    } +}; + +static int get_physical_address(CPUSPARCState *env, hwaddr *physical, +                                int *prot, int *access_index, +                                target_ulong address, int rw, int mmu_idx, +                                target_ulong *page_size) +{ +    int access_perms = 0; +    hwaddr pde_ptr; +    uint32_t pde; +    int error_code = 0, is_dirty, is_user; +    unsigned long page_offset; +    CPUState *cs = CPU(sparc_env_get_cpu(env)); + +    is_user = mmu_idx == MMU_USER_IDX; + +    if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ +        *page_size = TARGET_PAGE_SIZE; +        /* Boot mode: instruction fetches are taken from PROM */ +        if (rw == 2 && (env->mmuregs[0] & env->def->mmu_bm)) { +            *physical = env->prom_addr | (address & 0x7ffffULL); +            *prot = PAGE_READ | PAGE_EXEC; +            return 0; +        } +        *physical = address; +        *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; +        return 0; +    } + +    *access_index = ((rw & 1) << 2) | (rw & 2) | (is_user ? 0 : 1); +    *physical = 0xffffffffffff0000ULL; + +    /* SPARC reference MMU table walk: Context table->L1->L2->PTE */ +    /* Context base + context number */ +    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2); +    pde = ldl_phys(cs->as, pde_ptr); + +    /* Ctx pde */ +    switch (pde & PTE_ENTRYTYPE_MASK) { +    default: +    case 0: /* Invalid */ +        return 1 << 2; +    case 2: /* L0 PTE, maybe should not happen? */ +    case 3: /* Reserved */ +        return 4 << 2; +    case 1: /* L0 PDE */ +        pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4); +        pde = ldl_phys(cs->as, pde_ptr); + +        switch (pde & PTE_ENTRYTYPE_MASK) { +        default: +        case 0: /* Invalid */ +            return (1 << 8) | (1 << 2); +        case 3: /* Reserved */ +            return (1 << 8) | (4 << 2); +        case 1: /* L1 PDE */ +            pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); +            pde = ldl_phys(cs->as, pde_ptr); + +            switch (pde & PTE_ENTRYTYPE_MASK) { +            default: +            case 0: /* Invalid */ +                return (2 << 8) | (1 << 2); +            case 3: /* Reserved */ +                return (2 << 8) | (4 << 2); +            case 1: /* L2 PDE */ +                pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); +                pde = ldl_phys(cs->as, pde_ptr); + +                switch (pde & PTE_ENTRYTYPE_MASK) { +                default: +                case 0: /* Invalid */ +                    return (3 << 8) | (1 << 2); +                case 1: /* PDE, should not happen */ +                case 3: /* Reserved */ +                    return (3 << 8) | (4 << 2); +                case 2: /* L3 PTE */ +                    page_offset = 0; +                } +                *page_size = TARGET_PAGE_SIZE; +                break; +            case 2: /* L2 PTE */ +                page_offset = address & 0x3f000; +                *page_size = 0x40000; +            } +            break; +        case 2: /* L1 PTE */ +            page_offset = address & 0xfff000; +            *page_size = 0x1000000; +        } +    } + +    /* check access */ +    access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT; +    error_code = access_table[*access_index][access_perms]; +    if (error_code && !((env->mmuregs[0] & MMU_NF) && is_user)) { +        return error_code; +    } + +    /* update page modified and dirty bits */ +    is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK); +    if (!(pde & PG_ACCESSED_MASK) || is_dirty) { +        pde |= PG_ACCESSED_MASK; +        if (is_dirty) { +            pde |= PG_MODIFIED_MASK; +        } +        stl_phys_notdirty(cs->as, pde_ptr, pde); +    } + +    /* the page can be put in the TLB */ +    *prot = perm_table[is_user][access_perms]; +    if (!(pde & PG_MODIFIED_MASK)) { +        /* only set write access if already dirty... otherwise wait +           for dirty access */ +        *prot &= ~PAGE_WRITE; +    } + +    /* Even if large ptes, we map only one 4KB page in the cache to +       avoid filling it too fast */ +    *physical = ((hwaddr)(pde & PTE_ADDR_MASK) << 4) + page_offset; +    return error_code; +} + +/* Perform address translation */ +int sparc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, +                               int mmu_idx) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; +    hwaddr paddr; +    target_ulong vaddr; +    target_ulong page_size; +    int error_code = 0, prot, access_index; + +    address &= TARGET_PAGE_MASK; +    error_code = get_physical_address(env, &paddr, &prot, &access_index, +                                      address, rw, mmu_idx, &page_size); +    vaddr = address; +    if (error_code == 0) { +        qemu_log_mask(CPU_LOG_MMU, +                "Translate at %" VADDR_PRIx " -> " TARGET_FMT_plx ", vaddr " +                TARGET_FMT_lx "\n", address, paddr, vaddr); +        tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, page_size); +        return 0; +    } + +    if (env->mmuregs[3]) { /* Fault status register */ +        env->mmuregs[3] = 1; /* overflow (not read before another fault) */ +    } +    env->mmuregs[3] |= (access_index << 5) | error_code | 2; +    env->mmuregs[4] = address; /* Fault address register */ + +    if ((env->mmuregs[0] & MMU_NF) || env->psret == 0)  { +        /* No fault mode: if a mapping is available, just override +           permissions. If no mapping is available, redirect accesses to +           neverland. Fake/overridden mappings will be flushed when +           switching to normal mode. */ +        prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; +        tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, TARGET_PAGE_SIZE); +        return 0; +    } else { +        if (rw & 2) { +            cs->exception_index = TT_TFAULT; +        } else { +            cs->exception_index = TT_DFAULT; +        } +        return 1; +    } +} + +target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev) +{ +    CPUState *cs = CPU(sparc_env_get_cpu(env)); +    hwaddr pde_ptr; +    uint32_t pde; + +    /* Context base + context number */ +    pde_ptr = (hwaddr)(env->mmuregs[1] << 4) + +        (env->mmuregs[2] << 2); +    pde = ldl_phys(cs->as, pde_ptr); + +    switch (pde & PTE_ENTRYTYPE_MASK) { +    default: +    case 0: /* Invalid */ +    case 2: /* PTE, maybe should not happen? */ +    case 3: /* Reserved */ +        return 0; +    case 1: /* L1 PDE */ +        if (mmulev == 3) { +            return pde; +        } +        pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4); +        pde = ldl_phys(cs->as, pde_ptr); + +        switch (pde & PTE_ENTRYTYPE_MASK) { +        default: +        case 0: /* Invalid */ +        case 3: /* Reserved */ +            return 0; +        case 2: /* L1 PTE */ +            return pde; +        case 1: /* L2 PDE */ +            if (mmulev == 2) { +                return pde; +            } +            pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4); +            pde = ldl_phys(cs->as, pde_ptr); + +            switch (pde & PTE_ENTRYTYPE_MASK) { +            default: +            case 0: /* Invalid */ +            case 3: /* Reserved */ +                return 0; +            case 2: /* L2 PTE */ +                return pde; +            case 1: /* L3 PDE */ +                if (mmulev == 1) { +                    return pde; +                } +                pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4); +                pde = ldl_phys(cs->as, pde_ptr); + +                switch (pde & PTE_ENTRYTYPE_MASK) { +                default: +                case 0: /* Invalid */ +                case 1: /* PDE, should not happen */ +                case 3: /* Reserved */ +                    return 0; +                case 2: /* L3 PTE */ +                    return pde; +                } +            } +        } +    } +    return 0; +} + +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUSPARCState *env) +{ +    CPUState *cs = CPU(sparc_env_get_cpu(env)); +    target_ulong va, va1, va2; +    unsigned int n, m, o; +    hwaddr pde_ptr, pa; +    uint32_t pde; + +    pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2); +    pde = ldl_phys(cs->as, pde_ptr); +    (*cpu_fprintf)(f, "Root ptr: " TARGET_FMT_plx ", ctx: %d\n", +                   (hwaddr)env->mmuregs[1] << 4, env->mmuregs[2]); +    for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) { +        pde = mmu_probe(env, va, 2); +        if (pde) { +            pa = cpu_get_phys_page_debug(cs, va); +            (*cpu_fprintf)(f, "VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx +                           " PDE: " TARGET_FMT_lx "\n", va, pa, pde); +            for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) { +                pde = mmu_probe(env, va1, 1); +                if (pde) { +                    pa = cpu_get_phys_page_debug(cs, va1); +                    (*cpu_fprintf)(f, " VA: " TARGET_FMT_lx ", PA: " +                                   TARGET_FMT_plx " PDE: " TARGET_FMT_lx "\n", +                                   va1, pa, pde); +                    for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) { +                        pde = mmu_probe(env, va2, 0); +                        if (pde) { +                            pa = cpu_get_phys_page_debug(cs, va2); +                            (*cpu_fprintf)(f, "  VA: " TARGET_FMT_lx ", PA: " +                                           TARGET_FMT_plx " PTE: " +                                           TARGET_FMT_lx "\n", +                                           va2, pa, pde); +                        } +                    } +                } +            } +        } +    } +} + +/* Gdb expects all registers windows to be flushed in ram. This function handles + * reads (and only reads) in stack frames as if windows were flushed. We assume + * that the sparc ABI is followed. + */ +int sparc_cpu_memory_rw_debug(CPUState *cs, vaddr address, +                              uint8_t *buf, int len, bool is_write) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; +    target_ulong addr = address; +    int i; +    int len1; +    int cwp = env->cwp; + +    if (!is_write) { +        for (i = 0; i < env->nwindows; i++) { +            int off; +            target_ulong fp = env->regbase[cwp * 16 + 22]; + +            /* Assume fp == 0 means end of frame.  */ +            if (fp == 0) { +                break; +            } + +            cwp = cpu_cwp_inc(env, cwp + 1); + +            /* Invalid window ? */ +            if (env->wim & (1 << cwp)) { +                break; +            } + +            /* According to the ABI, the stack is growing downward.  */ +            if (addr + len < fp) { +                break; +            } + +            /* Not in this frame.  */ +            if (addr > fp + 64) { +                continue; +            } + +            /* Handle access before this window.  */ +            if (addr < fp) { +                len1 = fp - addr; +                if (cpu_memory_rw_debug(cs, addr, buf, len1, is_write) != 0) { +                    return -1; +                } +                addr += len1; +                len -= len1; +                buf += len1; +            } + +            /* Access byte per byte to registers. Not very efficient but speed +             * is not critical. +             */ +            off = addr - fp; +            len1 = 64 - off; + +            if (len1 > len) { +                len1 = len; +            } + +            for (; len1; len1--) { +                int reg = cwp * 16 + 8 + (off >> 2); +                union { +                    uint32_t v; +                    uint8_t c[4]; +                } u; +                u.v = cpu_to_be32(env->regbase[reg]); +                *buf++ = u.c[off & 3]; +                addr++; +                len--; +                off++; +            } + +            if (len == 0) { +                return 0; +            } +        } +    } +    return cpu_memory_rw_debug(cs, addr, buf, len, is_write); +} + +#else /* !TARGET_SPARC64 */ + +/* 41 bit physical address space */ +static inline hwaddr ultrasparc_truncate_physical(uint64_t x) +{ +    return x & 0x1ffffffffffULL; +} + +/* + * UltraSparc IIi I/DMMUs + */ + +/* Returns true if TTE tag is valid and matches virtual address value +   in context requires virtual address mask value calculated from TTE +   entry size */ +static inline int ultrasparc_tag_match(SparcTLBEntry *tlb, +                                       uint64_t address, uint64_t context, +                                       hwaddr *physical) +{ +    uint64_t mask; + +    switch (TTE_PGSIZE(tlb->tte)) { +    default: +    case 0x0: /* 8k */ +        mask = 0xffffffffffffe000ULL; +        break; +    case 0x1: /* 64k */ +        mask = 0xffffffffffff0000ULL; +        break; +    case 0x2: /* 512k */ +        mask = 0xfffffffffff80000ULL; +        break; +    case 0x3: /* 4M */ +        mask = 0xffffffffffc00000ULL; +        break; +    } + +    /* valid, context match, virtual address match? */ +    if (TTE_IS_VALID(tlb->tte) && +        (TTE_IS_GLOBAL(tlb->tte) || tlb_compare_context(tlb, context)) +        && compare_masked(address, tlb->tag, mask)) { +        /* decode physical address */ +        *physical = ((tlb->tte & mask) | (address & ~mask)) & 0x1ffffffe000ULL; +        return 1; +    } + +    return 0; +} + +static int get_physical_address_data(CPUSPARCState *env, +                                     hwaddr *physical, int *prot, +                                     target_ulong address, int rw, int mmu_idx) +{ +    CPUState *cs = CPU(sparc_env_get_cpu(env)); +    unsigned int i; +    uint64_t context; +    uint64_t sfsr = 0; + +    int is_user = (mmu_idx == MMU_USER_IDX || +                   mmu_idx == MMU_USER_SECONDARY_IDX); + +    if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */ +        *physical = ultrasparc_truncate_physical(address); +        *prot = PAGE_READ | PAGE_WRITE; +        return 0; +    } + +    switch (mmu_idx) { +    case MMU_USER_IDX: +    case MMU_KERNEL_IDX: +        context = env->dmmu.mmu_primary_context & 0x1fff; +        sfsr |= SFSR_CT_PRIMARY; +        break; +    case MMU_USER_SECONDARY_IDX: +    case MMU_KERNEL_SECONDARY_IDX: +        context = env->dmmu.mmu_secondary_context & 0x1fff; +        sfsr |= SFSR_CT_SECONDARY; +        break; +    case MMU_NUCLEUS_IDX: +        sfsr |= SFSR_CT_NUCLEUS; +        /* FALLTHRU */ +    default: +        context = 0; +        break; +    } + +    if (rw == 1) { +        sfsr |= SFSR_WRITE_BIT; +    } else if (rw == 4) { +        sfsr |= SFSR_NF_BIT; +    } + +    for (i = 0; i < 64; i++) { +        /* ctx match, vaddr match, valid? */ +        if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) { +            int do_fault = 0; + +            /* access ok? */ +            /* multiple bits in SFSR.FT may be set on TT_DFAULT */ +            if (TTE_IS_PRIV(env->dtlb[i].tte) && is_user) { +                do_fault = 1; +                sfsr |= SFSR_FT_PRIV_BIT; /* privilege violation */ +                trace_mmu_helper_dfault(address, context, mmu_idx, env->tl); +            } +            if (rw == 4) { +                if (TTE_IS_SIDEEFFECT(env->dtlb[i].tte)) { +                    do_fault = 1; +                    sfsr |= SFSR_FT_NF_E_BIT; +                } +            } else { +                if (TTE_IS_NFO(env->dtlb[i].tte)) { +                    do_fault = 1; +                    sfsr |= SFSR_FT_NFO_BIT; +                } +            } + +            if (do_fault) { +                /* faults above are reported with TT_DFAULT. */ +                cs->exception_index = TT_DFAULT; +            } else if (!TTE_IS_W_OK(env->dtlb[i].tte) && (rw == 1)) { +                do_fault = 1; +                cs->exception_index = TT_DPROT; + +                trace_mmu_helper_dprot(address, context, mmu_idx, env->tl); +            } + +            if (!do_fault) { +                *prot = PAGE_READ; +                if (TTE_IS_W_OK(env->dtlb[i].tte)) { +                    *prot |= PAGE_WRITE; +                } + +                TTE_SET_USED(env->dtlb[i].tte); + +                return 0; +            } + +            if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */ +                sfsr |= SFSR_OW_BIT; /* overflow (not read before +                                        another fault) */ +            } + +            if (env->pstate & PS_PRIV) { +                sfsr |= SFSR_PR_BIT; +            } + +            /* FIXME: ASI field in SFSR must be set */ +            env->dmmu.sfsr = sfsr | SFSR_VALID_BIT; + +            env->dmmu.sfar = address; /* Fault address register */ + +            env->dmmu.tag_access = (address & ~0x1fffULL) | context; + +            return 1; +        } +    } + +    trace_mmu_helper_dmiss(address, context); + +    /* +     * On MMU misses: +     * - UltraSPARC IIi: SFSR and SFAR unmodified +     * - JPS1: SFAR updated and some fields of SFSR updated +     */ +    env->dmmu.tag_access = (address & ~0x1fffULL) | context; +    cs->exception_index = TT_DMISS; +    return 1; +} + +static int get_physical_address_code(CPUSPARCState *env, +                                     hwaddr *physical, int *prot, +                                     target_ulong address, int mmu_idx) +{ +    CPUState *cs = CPU(sparc_env_get_cpu(env)); +    unsigned int i; +    uint64_t context; + +    int is_user = (mmu_idx == MMU_USER_IDX || +                   mmu_idx == MMU_USER_SECONDARY_IDX); + +    if ((env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0) { +        /* IMMU disabled */ +        *physical = ultrasparc_truncate_physical(address); +        *prot = PAGE_EXEC; +        return 0; +    } + +    if (env->tl == 0) { +        /* PRIMARY context */ +        context = env->dmmu.mmu_primary_context & 0x1fff; +    } else { +        /* NUCLEUS context */ +        context = 0; +    } + +    for (i = 0; i < 64; i++) { +        /* ctx match, vaddr match, valid? */ +        if (ultrasparc_tag_match(&env->itlb[i], +                                 address, context, physical)) { +            /* access ok? */ +            if (TTE_IS_PRIV(env->itlb[i].tte) && is_user) { +                /* Fault status register */ +                if (env->immu.sfsr & SFSR_VALID_BIT) { +                    env->immu.sfsr = SFSR_OW_BIT; /* overflow (not read before +                                                     another fault) */ +                } else { +                    env->immu.sfsr = 0; +                } +                if (env->pstate & PS_PRIV) { +                    env->immu.sfsr |= SFSR_PR_BIT; +                } +                if (env->tl > 0) { +                    env->immu.sfsr |= SFSR_CT_NUCLEUS; +                } + +                /* FIXME: ASI field in SFSR must be set */ +                env->immu.sfsr |= SFSR_FT_PRIV_BIT | SFSR_VALID_BIT; +                cs->exception_index = TT_TFAULT; + +                env->immu.tag_access = (address & ~0x1fffULL) | context; + +                trace_mmu_helper_tfault(address, context); + +                return 1; +            } +            *prot = PAGE_EXEC; +            TTE_SET_USED(env->itlb[i].tte); +            return 0; +        } +    } + +    trace_mmu_helper_tmiss(address, context); + +    /* Context is stored in DMMU (dmmuregs[1]) also for IMMU */ +    env->immu.tag_access = (address & ~0x1fffULL) | context; +    cs->exception_index = TT_TMISS; +    return 1; +} + +static int get_physical_address(CPUSPARCState *env, hwaddr *physical, +                                int *prot, int *access_index, +                                target_ulong address, int rw, int mmu_idx, +                                target_ulong *page_size) +{ +    /* ??? We treat everything as a small page, then explicitly flush +       everything when an entry is evicted.  */ +    *page_size = TARGET_PAGE_SIZE; + +    /* safety net to catch wrong softmmu index use from dynamic code */ +    if (env->tl > 0 && mmu_idx != MMU_NUCLEUS_IDX) { +        if (rw == 2) { +            trace_mmu_helper_get_phys_addr_code(env->tl, mmu_idx, +                                                env->dmmu.mmu_primary_context, +                                                env->dmmu.mmu_secondary_context, +                                                address); +        } else { +            trace_mmu_helper_get_phys_addr_data(env->tl, mmu_idx, +                                                env->dmmu.mmu_primary_context, +                                                env->dmmu.mmu_secondary_context, +                                                address); +        } +    } + +    if (rw == 2) { +        return get_physical_address_code(env, physical, prot, address, +                                         mmu_idx); +    } else { +        return get_physical_address_data(env, physical, prot, address, rw, +                                         mmu_idx); +    } +} + +/* Perform address translation */ +int sparc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, +                               int mmu_idx) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; +    target_ulong vaddr; +    hwaddr paddr; +    target_ulong page_size; +    int error_code = 0, prot, access_index; + +    address &= TARGET_PAGE_MASK; +    error_code = get_physical_address(env, &paddr, &prot, &access_index, +                                      address, rw, mmu_idx, &page_size); +    if (error_code == 0) { +        vaddr = address; + +        trace_mmu_helper_mmu_fault(address, paddr, mmu_idx, env->tl, +                                   env->dmmu.mmu_primary_context, +                                   env->dmmu.mmu_secondary_context); + +        tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, page_size); +        return 0; +    } +    /* XXX */ +    return 1; +} + +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUSPARCState *env) +{ +    unsigned int i; +    const char *mask; + +    (*cpu_fprintf)(f, "MMU contexts: Primary: %" PRId64 ", Secondary: %" +                   PRId64 "\n", +                   env->dmmu.mmu_primary_context, +                   env->dmmu.mmu_secondary_context); +    if ((env->lsu & DMMU_E) == 0) { +        (*cpu_fprintf)(f, "DMMU disabled\n"); +    } else { +        (*cpu_fprintf)(f, "DMMU dump\n"); +        for (i = 0; i < 64; i++) { +            switch (TTE_PGSIZE(env->dtlb[i].tte)) { +            default: +            case 0x0: +                mask = "  8k"; +                break; +            case 0x1: +                mask = " 64k"; +                break; +            case 0x2: +                mask = "512k"; +                break; +            case 0x3: +                mask = "  4M"; +                break; +            } +            if (TTE_IS_VALID(env->dtlb[i].tte)) { +                (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx" +                               ", %s, %s, %s, %s, ctx %" PRId64 " %s\n", +                               i, +                               env->dtlb[i].tag & (uint64_t)~0x1fffULL, +                               TTE_PA(env->dtlb[i].tte), +                               mask, +                               TTE_IS_PRIV(env->dtlb[i].tte) ? "priv" : "user", +                               TTE_IS_W_OK(env->dtlb[i].tte) ? "RW" : "RO", +                               TTE_IS_LOCKED(env->dtlb[i].tte) ? +                               "locked" : "unlocked", +                               env->dtlb[i].tag & (uint64_t)0x1fffULL, +                               TTE_IS_GLOBAL(env->dtlb[i].tte) ? +                               "global" : "local"); +            } +        } +    } +    if ((env->lsu & IMMU_E) == 0) { +        (*cpu_fprintf)(f, "IMMU disabled\n"); +    } else { +        (*cpu_fprintf)(f, "IMMU dump\n"); +        for (i = 0; i < 64; i++) { +            switch (TTE_PGSIZE(env->itlb[i].tte)) { +            default: +            case 0x0: +                mask = "  8k"; +                break; +            case 0x1: +                mask = " 64k"; +                break; +            case 0x2: +                mask = "512k"; +                break; +            case 0x3: +                mask = "  4M"; +                break; +            } +            if (TTE_IS_VALID(env->itlb[i].tte)) { +                (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx" +                               ", %s, %s, %s, ctx %" PRId64 " %s\n", +                               i, +                               env->itlb[i].tag & (uint64_t)~0x1fffULL, +                               TTE_PA(env->itlb[i].tte), +                               mask, +                               TTE_IS_PRIV(env->itlb[i].tte) ? "priv" : "user", +                               TTE_IS_LOCKED(env->itlb[i].tte) ? +                               "locked" : "unlocked", +                               env->itlb[i].tag & (uint64_t)0x1fffULL, +                               TTE_IS_GLOBAL(env->itlb[i].tte) ? +                               "global" : "local"); +            } +        } +    } +} + +#endif /* TARGET_SPARC64 */ + +static int cpu_sparc_get_phys_page(CPUSPARCState *env, hwaddr *phys, +                                   target_ulong addr, int rw, int mmu_idx) +{ +    target_ulong page_size; +    int prot, access_index; + +    return get_physical_address(env, phys, &prot, &access_index, addr, rw, +                                mmu_idx, &page_size); +} + +#if defined(TARGET_SPARC64) +hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr, +                                           int mmu_idx) +{ +    hwaddr phys_addr; + +    if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 4, mmu_idx) != 0) { +        return -1; +    } +    return phys_addr; +} +#endif + +hwaddr sparc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ +    SPARCCPU *cpu = SPARC_CPU(cs); +    CPUSPARCState *env = &cpu->env; +    hwaddr phys_addr; +    int mmu_idx = cpu_mmu_index(env); +    MemoryRegionSection section; + +    if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) { +        if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) { +            return -1; +        } +    } +    section = memory_region_find(get_system_memory(), phys_addr, 1); +    memory_region_unref(section.mr); +    if (!int128_nz(section.size)) { +        return -1; +    } +    return phys_addr; +} +#endif diff --git a/target-sparc/translate.c b/target-sparc/translate.c new file mode 100644 index 00000000..c58dd4e9 --- /dev/null +++ b/target-sparc/translate.c @@ -0,0 +1,5471 @@ +/* +   SPARC translation + +   Copyright (C) 2003 Thomas M. Ogrisegg <tom@fnord.at> +   Copyright (C) 2003-2005 Fabrice Bellard + +   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 "cpu.h" +#include "disas/disas.h" +#include "exec/helper-proto.h" +#include "tcg-op.h" +#include "exec/cpu_ldst.h" + +#include "exec/helper-gen.h" + +#include "trace-tcg.h" + + +#define DEBUG_DISAS + +#define DYNAMIC_PC  1 /* dynamic pc value */ +#define JUMP_PC     2 /* dynamic pc value which takes only two values +                         according to jump_pc[T2] */ + +/* global register indexes */ +static TCGv_ptr cpu_env, cpu_regwptr; +static TCGv cpu_cc_src, cpu_cc_src2, cpu_cc_dst; +static TCGv_i32 cpu_cc_op; +static TCGv_i32 cpu_psr; +static TCGv cpu_fsr, cpu_pc, cpu_npc, cpu_gregs[8]; +static TCGv cpu_y; +#ifndef CONFIG_USER_ONLY +static TCGv cpu_tbr; +#endif +static TCGv cpu_cond; +#ifdef TARGET_SPARC64 +static TCGv_i32 cpu_xcc, cpu_asi, cpu_fprs; +static TCGv cpu_gsr; +static TCGv cpu_tick_cmpr, cpu_stick_cmpr, cpu_hstick_cmpr; +static TCGv cpu_hintp, cpu_htba, cpu_hver, cpu_ssr, cpu_ver; +static TCGv_i32 cpu_softint; +#else +static TCGv cpu_wim; +#endif +/* Floating point registers */ +static TCGv_i64 cpu_fpr[TARGET_DPREGS]; + +static target_ulong gen_opc_npc[OPC_BUF_SIZE]; +static target_ulong gen_opc_jump_pc[2]; + +#include "exec/gen-icount.h" + +typedef struct DisasContext { +    target_ulong pc;    /* current Program Counter: integer or DYNAMIC_PC */ +    target_ulong npc;   /* next PC: integer or DYNAMIC_PC or JUMP_PC */ +    target_ulong jump_pc[2]; /* used when JUMP_PC pc value is used */ +    int is_br; +    int mem_idx; +    int fpu_enabled; +    int address_mask_32bit; +    int singlestep; +    uint32_t cc_op;  /* current CC operation */ +    struct TranslationBlock *tb; +    sparc_def_t *def; +    TCGv_i32 t32[3]; +    TCGv ttl[5]; +    int n_t32; +    int n_ttl; +} DisasContext; + +typedef struct { +    TCGCond cond; +    bool is_bool; +    bool g1, g2; +    TCGv c1, c2; +} DisasCompare; + +// This function uses non-native bit order +#define GET_FIELD(X, FROM, TO)                                  \ +    ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1)) + +// This function uses the order in the manuals, i.e. bit 0 is 2^0 +#define GET_FIELD_SP(X, FROM, TO)               \ +    GET_FIELD(X, 31 - (TO), 31 - (FROM)) + +#define GET_FIELDs(x,a,b) sign_extend (GET_FIELD(x,a,b), (b) - (a) + 1) +#define GET_FIELD_SPs(x,a,b) sign_extend (GET_FIELD_SP(x,a,b), ((b) - (a) + 1)) + +#ifdef TARGET_SPARC64 +#define DFPREG(r) (((r & 1) << 5) | (r & 0x1e)) +#define QFPREG(r) (((r & 1) << 5) | (r & 0x1c)) +#else +#define DFPREG(r) (r & 0x1e) +#define QFPREG(r) (r & 0x1c) +#endif + +#define UA2005_HTRAP_MASK 0xff +#define V8_TRAP_MASK 0x7f + +static int sign_extend(int x, int len) +{ +    len = 32 - len; +    return (x << len) >> len; +} + +#define IS_IMM (insn & (1<<13)) + +static inline TCGv_i32 get_temp_i32(DisasContext *dc) +{ +    TCGv_i32 t; +    assert(dc->n_t32 < ARRAY_SIZE(dc->t32)); +    dc->t32[dc->n_t32++] = t = tcg_temp_new_i32(); +    return t; +} + +static inline TCGv get_temp_tl(DisasContext *dc) +{ +    TCGv t; +    assert(dc->n_ttl < ARRAY_SIZE(dc->ttl)); +    dc->ttl[dc->n_ttl++] = t = tcg_temp_new(); +    return t; +} + +static inline void gen_update_fprs_dirty(int rd) +{ +#if defined(TARGET_SPARC64) +    tcg_gen_ori_i32(cpu_fprs, cpu_fprs, (rd < 32) ? 1 : 2); +#endif +} + +/* floating point registers moves */ +static TCGv_i32 gen_load_fpr_F(DisasContext *dc, unsigned int src) +{ +#if TCG_TARGET_REG_BITS == 32 +    if (src & 1) { +        return TCGV_LOW(cpu_fpr[src / 2]); +    } else { +        return TCGV_HIGH(cpu_fpr[src / 2]); +    } +#else +    if (src & 1) { +        return MAKE_TCGV_I32(GET_TCGV_I64(cpu_fpr[src / 2])); +    } else { +        TCGv_i32 ret = get_temp_i32(dc); +        TCGv_i64 t = tcg_temp_new_i64(); + +        tcg_gen_shri_i64(t, cpu_fpr[src / 2], 32); +        tcg_gen_trunc_i64_i32(ret, t); +        tcg_temp_free_i64(t); + +        return ret; +    } +#endif +} + +static void gen_store_fpr_F(DisasContext *dc, unsigned int dst, TCGv_i32 v) +{ +#if TCG_TARGET_REG_BITS == 32 +    if (dst & 1) { +        tcg_gen_mov_i32(TCGV_LOW(cpu_fpr[dst / 2]), v); +    } else { +        tcg_gen_mov_i32(TCGV_HIGH(cpu_fpr[dst / 2]), v); +    } +#else +    TCGv_i64 t = MAKE_TCGV_I64(GET_TCGV_I32(v)); +    tcg_gen_deposit_i64(cpu_fpr[dst / 2], cpu_fpr[dst / 2], t, +                        (dst & 1 ? 0 : 32), 32); +#endif +    gen_update_fprs_dirty(dst); +} + +static TCGv_i32 gen_dest_fpr_F(DisasContext *dc) +{ +    return get_temp_i32(dc); +} + +static TCGv_i64 gen_load_fpr_D(DisasContext *dc, unsigned int src) +{ +    src = DFPREG(src); +    return cpu_fpr[src / 2]; +} + +static void gen_store_fpr_D(DisasContext *dc, unsigned int dst, TCGv_i64 v) +{ +    dst = DFPREG(dst); +    tcg_gen_mov_i64(cpu_fpr[dst / 2], v); +    gen_update_fprs_dirty(dst); +} + +static TCGv_i64 gen_dest_fpr_D(DisasContext *dc, unsigned int dst) +{ +    return cpu_fpr[DFPREG(dst) / 2]; +} + +static void gen_op_load_fpr_QT0(unsigned int src) +{ +    tcg_gen_st_i64(cpu_fpr[src / 2], cpu_env, offsetof(CPUSPARCState, qt0) + +                   offsetof(CPU_QuadU, ll.upper)); +    tcg_gen_st_i64(cpu_fpr[src/2 + 1], cpu_env, offsetof(CPUSPARCState, qt0) + +                   offsetof(CPU_QuadU, ll.lower)); +} + +static void gen_op_load_fpr_QT1(unsigned int src) +{ +    tcg_gen_st_i64(cpu_fpr[src / 2], cpu_env, offsetof(CPUSPARCState, qt1) + +                   offsetof(CPU_QuadU, ll.upper)); +    tcg_gen_st_i64(cpu_fpr[src/2 + 1], cpu_env, offsetof(CPUSPARCState, qt1) + +                   offsetof(CPU_QuadU, ll.lower)); +} + +static void gen_op_store_QT0_fpr(unsigned int dst) +{ +    tcg_gen_ld_i64(cpu_fpr[dst / 2], cpu_env, offsetof(CPUSPARCState, qt0) + +                   offsetof(CPU_QuadU, ll.upper)); +    tcg_gen_ld_i64(cpu_fpr[dst/2 + 1], cpu_env, offsetof(CPUSPARCState, qt0) + +                   offsetof(CPU_QuadU, ll.lower)); +} + +#ifdef TARGET_SPARC64 +static void gen_move_Q(unsigned int rd, unsigned int rs) +{ +    rd = QFPREG(rd); +    rs = QFPREG(rs); + +    tcg_gen_mov_i64(cpu_fpr[rd / 2], cpu_fpr[rs / 2]); +    tcg_gen_mov_i64(cpu_fpr[rd / 2 + 1], cpu_fpr[rs / 2 + 1]); +    gen_update_fprs_dirty(rd); +} +#endif + +/* moves */ +#ifdef CONFIG_USER_ONLY +#define supervisor(dc) 0 +#ifdef TARGET_SPARC64 +#define hypervisor(dc) 0 +#endif +#else +#define supervisor(dc) (dc->mem_idx >= MMU_KERNEL_IDX) +#ifdef TARGET_SPARC64 +#define hypervisor(dc) (dc->mem_idx == MMU_HYPV_IDX) +#else +#endif +#endif + +#ifdef TARGET_SPARC64 +#ifndef TARGET_ABI32 +#define AM_CHECK(dc) ((dc)->address_mask_32bit) +#else +#define AM_CHECK(dc) (1) +#endif +#endif + +static inline void gen_address_mask(DisasContext *dc, TCGv addr) +{ +#ifdef TARGET_SPARC64 +    if (AM_CHECK(dc)) +        tcg_gen_andi_tl(addr, addr, 0xffffffffULL); +#endif +} + +static inline TCGv gen_load_gpr(DisasContext *dc, int reg) +{ +    if (reg == 0 || reg >= 8) { +        TCGv t = get_temp_tl(dc); +        if (reg == 0) { +            tcg_gen_movi_tl(t, 0); +        } else { +            tcg_gen_ld_tl(t, cpu_regwptr, (reg - 8) * sizeof(target_ulong)); +        } +        return t; +    } else { +        return cpu_gregs[reg]; +    } +} + +static inline void gen_store_gpr(DisasContext *dc, int reg, TCGv v) +{ +    if (reg > 0) { +        if (reg < 8) { +            tcg_gen_mov_tl(cpu_gregs[reg], v); +        } else { +            tcg_gen_st_tl(v, cpu_regwptr, (reg - 8) * sizeof(target_ulong)); +        } +    } +} + +static inline TCGv gen_dest_gpr(DisasContext *dc, int reg) +{ +    if (reg == 0 || reg >= 8) { +        return get_temp_tl(dc); +    } else { +        return cpu_gregs[reg]; +    } +} + +static inline void gen_goto_tb(DisasContext *s, int tb_num, +                               target_ulong pc, target_ulong npc) +{ +    TranslationBlock *tb; + +    tb = s->tb; +    if ((pc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) && +        (npc & TARGET_PAGE_MASK) == (tb->pc & TARGET_PAGE_MASK) && +        !s->singlestep)  { +        /* jump to same page: we can use a direct jump */ +        tcg_gen_goto_tb(tb_num); +        tcg_gen_movi_tl(cpu_pc, pc); +        tcg_gen_movi_tl(cpu_npc, npc); +        tcg_gen_exit_tb((uintptr_t)tb + tb_num); +    } else { +        /* jump to another page: currently not optimized */ +        tcg_gen_movi_tl(cpu_pc, pc); +        tcg_gen_movi_tl(cpu_npc, npc); +        tcg_gen_exit_tb(0); +    } +} + +// XXX suboptimal +static inline void gen_mov_reg_N(TCGv reg, TCGv_i32 src) +{ +    tcg_gen_extu_i32_tl(reg, src); +    tcg_gen_shri_tl(reg, reg, PSR_NEG_SHIFT); +    tcg_gen_andi_tl(reg, reg, 0x1); +} + +static inline void gen_mov_reg_Z(TCGv reg, TCGv_i32 src) +{ +    tcg_gen_extu_i32_tl(reg, src); +    tcg_gen_shri_tl(reg, reg, PSR_ZERO_SHIFT); +    tcg_gen_andi_tl(reg, reg, 0x1); +} + +static inline void gen_mov_reg_V(TCGv reg, TCGv_i32 src) +{ +    tcg_gen_extu_i32_tl(reg, src); +    tcg_gen_shri_tl(reg, reg, PSR_OVF_SHIFT); +    tcg_gen_andi_tl(reg, reg, 0x1); +} + +static inline void gen_mov_reg_C(TCGv reg, TCGv_i32 src) +{ +    tcg_gen_extu_i32_tl(reg, src); +    tcg_gen_shri_tl(reg, reg, PSR_CARRY_SHIFT); +    tcg_gen_andi_tl(reg, reg, 0x1); +} + +static inline void gen_op_add_cc(TCGv dst, TCGv src1, TCGv src2) +{ +    tcg_gen_mov_tl(cpu_cc_src, src1); +    tcg_gen_mov_tl(cpu_cc_src2, src2); +    tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2); +    tcg_gen_mov_tl(dst, cpu_cc_dst); +} + +static TCGv_i32 gen_add32_carry32(void) +{ +    TCGv_i32 carry_32, cc_src1_32, cc_src2_32; + +    /* Carry is computed from a previous add: (dst < src)  */ +#if TARGET_LONG_BITS == 64 +    cc_src1_32 = tcg_temp_new_i32(); +    cc_src2_32 = tcg_temp_new_i32(); +    tcg_gen_trunc_i64_i32(cc_src1_32, cpu_cc_dst); +    tcg_gen_trunc_i64_i32(cc_src2_32, cpu_cc_src); +#else +    cc_src1_32 = cpu_cc_dst; +    cc_src2_32 = cpu_cc_src; +#endif + +    carry_32 = tcg_temp_new_i32(); +    tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32); + +#if TARGET_LONG_BITS == 64 +    tcg_temp_free_i32(cc_src1_32); +    tcg_temp_free_i32(cc_src2_32); +#endif + +    return carry_32; +} + +static TCGv_i32 gen_sub32_carry32(void) +{ +    TCGv_i32 carry_32, cc_src1_32, cc_src2_32; + +    /* Carry is computed from a previous borrow: (src1 < src2)  */ +#if TARGET_LONG_BITS == 64 +    cc_src1_32 = tcg_temp_new_i32(); +    cc_src2_32 = tcg_temp_new_i32(); +    tcg_gen_trunc_i64_i32(cc_src1_32, cpu_cc_src); +    tcg_gen_trunc_i64_i32(cc_src2_32, cpu_cc_src2); +#else +    cc_src1_32 = cpu_cc_src; +    cc_src2_32 = cpu_cc_src2; +#endif + +    carry_32 = tcg_temp_new_i32(); +    tcg_gen_setcond_i32(TCG_COND_LTU, carry_32, cc_src1_32, cc_src2_32); + +#if TARGET_LONG_BITS == 64 +    tcg_temp_free_i32(cc_src1_32); +    tcg_temp_free_i32(cc_src2_32); +#endif + +    return carry_32; +} + +static void gen_op_addx_int(DisasContext *dc, TCGv dst, TCGv src1, +                            TCGv src2, int update_cc) +{ +    TCGv_i32 carry_32; +    TCGv carry; + +    switch (dc->cc_op) { +    case CC_OP_DIV: +    case CC_OP_LOGIC: +        /* Carry is known to be zero.  Fall back to plain ADD.  */ +        if (update_cc) { +            gen_op_add_cc(dst, src1, src2); +        } else { +            tcg_gen_add_tl(dst, src1, src2); +        } +        return; + +    case CC_OP_ADD: +    case CC_OP_TADD: +    case CC_OP_TADDTV: +        if (TARGET_LONG_BITS == 32) { +            /* We can re-use the host's hardware carry generation by using +               an ADD2 opcode.  We discard the low part of the output. +               Ideally we'd combine this operation with the add that +               generated the carry in the first place.  */ +            carry = tcg_temp_new(); +            tcg_gen_add2_tl(carry, dst, cpu_cc_src, src1, cpu_cc_src2, src2); +            tcg_temp_free(carry); +            goto add_done; +        } +        carry_32 = gen_add32_carry32(); +        break; + +    case CC_OP_SUB: +    case CC_OP_TSUB: +    case CC_OP_TSUBTV: +        carry_32 = gen_sub32_carry32(); +        break; + +    default: +        /* We need external help to produce the carry.  */ +        carry_32 = tcg_temp_new_i32(); +        gen_helper_compute_C_icc(carry_32, cpu_env); +        break; +    } + +#if TARGET_LONG_BITS == 64 +    carry = tcg_temp_new(); +    tcg_gen_extu_i32_i64(carry, carry_32); +#else +    carry = carry_32; +#endif + +    tcg_gen_add_tl(dst, src1, src2); +    tcg_gen_add_tl(dst, dst, carry); + +    tcg_temp_free_i32(carry_32); +#if TARGET_LONG_BITS == 64 +    tcg_temp_free(carry); +#endif + + add_done: +    if (update_cc) { +        tcg_gen_mov_tl(cpu_cc_src, src1); +        tcg_gen_mov_tl(cpu_cc_src2, src2); +        tcg_gen_mov_tl(cpu_cc_dst, dst); +        tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADDX); +        dc->cc_op = CC_OP_ADDX; +    } +} + +static inline void gen_op_sub_cc(TCGv dst, TCGv src1, TCGv src2) +{ +    tcg_gen_mov_tl(cpu_cc_src, src1); +    tcg_gen_mov_tl(cpu_cc_src2, src2); +    tcg_gen_sub_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2); +    tcg_gen_mov_tl(dst, cpu_cc_dst); +} + +static void gen_op_subx_int(DisasContext *dc, TCGv dst, TCGv src1, +                            TCGv src2, int update_cc) +{ +    TCGv_i32 carry_32; +    TCGv carry; + +    switch (dc->cc_op) { +    case CC_OP_DIV: +    case CC_OP_LOGIC: +        /* Carry is known to be zero.  Fall back to plain SUB.  */ +        if (update_cc) { +            gen_op_sub_cc(dst, src1, src2); +        } else { +            tcg_gen_sub_tl(dst, src1, src2); +        } +        return; + +    case CC_OP_ADD: +    case CC_OP_TADD: +    case CC_OP_TADDTV: +        carry_32 = gen_add32_carry32(); +        break; + +    case CC_OP_SUB: +    case CC_OP_TSUB: +    case CC_OP_TSUBTV: +        if (TARGET_LONG_BITS == 32) { +            /* We can re-use the host's hardware carry generation by using +               a SUB2 opcode.  We discard the low part of the output. +               Ideally we'd combine this operation with the add that +               generated the carry in the first place.  */ +            carry = tcg_temp_new(); +            tcg_gen_sub2_tl(carry, dst, cpu_cc_src, src1, cpu_cc_src2, src2); +            tcg_temp_free(carry); +            goto sub_done; +        } +        carry_32 = gen_sub32_carry32(); +        break; + +    default: +        /* We need external help to produce the carry.  */ +        carry_32 = tcg_temp_new_i32(); +        gen_helper_compute_C_icc(carry_32, cpu_env); +        break; +    } + +#if TARGET_LONG_BITS == 64 +    carry = tcg_temp_new(); +    tcg_gen_extu_i32_i64(carry, carry_32); +#else +    carry = carry_32; +#endif + +    tcg_gen_sub_tl(dst, src1, src2); +    tcg_gen_sub_tl(dst, dst, carry); + +    tcg_temp_free_i32(carry_32); +#if TARGET_LONG_BITS == 64 +    tcg_temp_free(carry); +#endif + + sub_done: +    if (update_cc) { +        tcg_gen_mov_tl(cpu_cc_src, src1); +        tcg_gen_mov_tl(cpu_cc_src2, src2); +        tcg_gen_mov_tl(cpu_cc_dst, dst); +        tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUBX); +        dc->cc_op = CC_OP_SUBX; +    } +} + +static inline void gen_op_mulscc(TCGv dst, TCGv src1, TCGv src2) +{ +    TCGv r_temp, zero, t0; + +    r_temp = tcg_temp_new(); +    t0 = tcg_temp_new(); + +    /* old op: +    if (!(env->y & 1)) +        T1 = 0; +    */ +    zero = tcg_const_tl(0); +    tcg_gen_andi_tl(cpu_cc_src, src1, 0xffffffff); +    tcg_gen_andi_tl(r_temp, cpu_y, 0x1); +    tcg_gen_andi_tl(cpu_cc_src2, src2, 0xffffffff); +    tcg_gen_movcond_tl(TCG_COND_EQ, cpu_cc_src2, r_temp, zero, +                       zero, cpu_cc_src2); +    tcg_temp_free(zero); + +    // b2 = T0 & 1; +    // env->y = (b2 << 31) | (env->y >> 1); +    tcg_gen_andi_tl(r_temp, cpu_cc_src, 0x1); +    tcg_gen_shli_tl(r_temp, r_temp, 31); +    tcg_gen_shri_tl(t0, cpu_y, 1); +    tcg_gen_andi_tl(t0, t0, 0x7fffffff); +    tcg_gen_or_tl(t0, t0, r_temp); +    tcg_gen_andi_tl(cpu_y, t0, 0xffffffff); + +    // b1 = N ^ V; +    gen_mov_reg_N(t0, cpu_psr); +    gen_mov_reg_V(r_temp, cpu_psr); +    tcg_gen_xor_tl(t0, t0, r_temp); +    tcg_temp_free(r_temp); + +    // T0 = (b1 << 31) | (T0 >> 1); +    // src1 = T0; +    tcg_gen_shli_tl(t0, t0, 31); +    tcg_gen_shri_tl(cpu_cc_src, cpu_cc_src, 1); +    tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t0); +    tcg_temp_free(t0); + +    tcg_gen_add_tl(cpu_cc_dst, cpu_cc_src, cpu_cc_src2); + +    tcg_gen_mov_tl(dst, cpu_cc_dst); +} + +static inline void gen_op_multiply(TCGv dst, TCGv src1, TCGv src2, int sign_ext) +{ +#if TARGET_LONG_BITS == 32 +    if (sign_ext) { +        tcg_gen_muls2_tl(dst, cpu_y, src1, src2); +    } else { +        tcg_gen_mulu2_tl(dst, cpu_y, src1, src2); +    } +#else +    TCGv t0 = tcg_temp_new_i64(); +    TCGv t1 = tcg_temp_new_i64(); + +    if (sign_ext) { +        tcg_gen_ext32s_i64(t0, src1); +        tcg_gen_ext32s_i64(t1, src2); +    } else { +        tcg_gen_ext32u_i64(t0, src1); +        tcg_gen_ext32u_i64(t1, src2); +    } + +    tcg_gen_mul_i64(dst, t0, t1); +    tcg_temp_free(t0); +    tcg_temp_free(t1); + +    tcg_gen_shri_i64(cpu_y, dst, 32); +#endif +} + +static inline void gen_op_umul(TCGv dst, TCGv src1, TCGv src2) +{ +    /* zero-extend truncated operands before multiplication */ +    gen_op_multiply(dst, src1, src2, 0); +} + +static inline void gen_op_smul(TCGv dst, TCGv src1, TCGv src2) +{ +    /* sign-extend truncated operands before multiplication */ +    gen_op_multiply(dst, src1, src2, 1); +} + +// 1 +static inline void gen_op_eval_ba(TCGv dst) +{ +    tcg_gen_movi_tl(dst, 1); +} + +// Z +static inline void gen_op_eval_be(TCGv dst, TCGv_i32 src) +{ +    gen_mov_reg_Z(dst, src); +} + +// Z | (N ^ V) +static inline void gen_op_eval_ble(TCGv dst, TCGv_i32 src) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_N(t0, src); +    gen_mov_reg_V(dst, src); +    tcg_gen_xor_tl(dst, dst, t0); +    gen_mov_reg_Z(t0, src); +    tcg_gen_or_tl(dst, dst, t0); +    tcg_temp_free(t0); +} + +// N ^ V +static inline void gen_op_eval_bl(TCGv dst, TCGv_i32 src) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_V(t0, src); +    gen_mov_reg_N(dst, src); +    tcg_gen_xor_tl(dst, dst, t0); +    tcg_temp_free(t0); +} + +// C | Z +static inline void gen_op_eval_bleu(TCGv dst, TCGv_i32 src) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_Z(t0, src); +    gen_mov_reg_C(dst, src); +    tcg_gen_or_tl(dst, dst, t0); +    tcg_temp_free(t0); +} + +// C +static inline void gen_op_eval_bcs(TCGv dst, TCGv_i32 src) +{ +    gen_mov_reg_C(dst, src); +} + +// V +static inline void gen_op_eval_bvs(TCGv dst, TCGv_i32 src) +{ +    gen_mov_reg_V(dst, src); +} + +// 0 +static inline void gen_op_eval_bn(TCGv dst) +{ +    tcg_gen_movi_tl(dst, 0); +} + +// N +static inline void gen_op_eval_bneg(TCGv dst, TCGv_i32 src) +{ +    gen_mov_reg_N(dst, src); +} + +// !Z +static inline void gen_op_eval_bne(TCGv dst, TCGv_i32 src) +{ +    gen_mov_reg_Z(dst, src); +    tcg_gen_xori_tl(dst, dst, 0x1); +} + +// !(Z | (N ^ V)) +static inline void gen_op_eval_bg(TCGv dst, TCGv_i32 src) +{ +    gen_op_eval_ble(dst, src); +    tcg_gen_xori_tl(dst, dst, 0x1); +} + +// !(N ^ V) +static inline void gen_op_eval_bge(TCGv dst, TCGv_i32 src) +{ +    gen_op_eval_bl(dst, src); +    tcg_gen_xori_tl(dst, dst, 0x1); +} + +// !(C | Z) +static inline void gen_op_eval_bgu(TCGv dst, TCGv_i32 src) +{ +    gen_op_eval_bleu(dst, src); +    tcg_gen_xori_tl(dst, dst, 0x1); +} + +// !C +static inline void gen_op_eval_bcc(TCGv dst, TCGv_i32 src) +{ +    gen_mov_reg_C(dst, src); +    tcg_gen_xori_tl(dst, dst, 0x1); +} + +// !N +static inline void gen_op_eval_bpos(TCGv dst, TCGv_i32 src) +{ +    gen_mov_reg_N(dst, src); +    tcg_gen_xori_tl(dst, dst, 0x1); +} + +// !V +static inline void gen_op_eval_bvc(TCGv dst, TCGv_i32 src) +{ +    gen_mov_reg_V(dst, src); +    tcg_gen_xori_tl(dst, dst, 0x1); +} + +/* +  FPSR bit field FCC1 | FCC0: +   0 = +   1 < +   2 > +   3 unordered +*/ +static inline void gen_mov_reg_FCC0(TCGv reg, TCGv src, +                                    unsigned int fcc_offset) +{ +    tcg_gen_shri_tl(reg, src, FSR_FCC0_SHIFT + fcc_offset); +    tcg_gen_andi_tl(reg, reg, 0x1); +} + +static inline void gen_mov_reg_FCC1(TCGv reg, TCGv src, +                                    unsigned int fcc_offset) +{ +    tcg_gen_shri_tl(reg, src, FSR_FCC1_SHIFT + fcc_offset); +    tcg_gen_andi_tl(reg, reg, 0x1); +} + +// !0: FCC0 | FCC1 +static inline void gen_op_eval_fbne(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    gen_mov_reg_FCC1(t0, src, fcc_offset); +    tcg_gen_or_tl(dst, dst, t0); +    tcg_temp_free(t0); +} + +// 1 or 2: FCC0 ^ FCC1 +static inline void gen_op_eval_fblg(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    gen_mov_reg_FCC1(t0, src, fcc_offset); +    tcg_gen_xor_tl(dst, dst, t0); +    tcg_temp_free(t0); +} + +// 1 or 3: FCC0 +static inline void gen_op_eval_fbul(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    gen_mov_reg_FCC0(dst, src, fcc_offset); +} + +// 1: FCC0 & !FCC1 +static inline void gen_op_eval_fbl(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    gen_mov_reg_FCC1(t0, src, fcc_offset); +    tcg_gen_andc_tl(dst, dst, t0); +    tcg_temp_free(t0); +} + +// 2 or 3: FCC1 +static inline void gen_op_eval_fbug(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    gen_mov_reg_FCC1(dst, src, fcc_offset); +} + +// 2: !FCC0 & FCC1 +static inline void gen_op_eval_fbg(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    gen_mov_reg_FCC1(t0, src, fcc_offset); +    tcg_gen_andc_tl(dst, t0, dst); +    tcg_temp_free(t0); +} + +// 3: FCC0 & FCC1 +static inline void gen_op_eval_fbu(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    gen_mov_reg_FCC1(t0, src, fcc_offset); +    tcg_gen_and_tl(dst, dst, t0); +    tcg_temp_free(t0); +} + +// 0: !(FCC0 | FCC1) +static inline void gen_op_eval_fbe(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    gen_mov_reg_FCC1(t0, src, fcc_offset); +    tcg_gen_or_tl(dst, dst, t0); +    tcg_gen_xori_tl(dst, dst, 0x1); +    tcg_temp_free(t0); +} + +// 0 or 3: !(FCC0 ^ FCC1) +static inline void gen_op_eval_fbue(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    gen_mov_reg_FCC1(t0, src, fcc_offset); +    tcg_gen_xor_tl(dst, dst, t0); +    tcg_gen_xori_tl(dst, dst, 0x1); +    tcg_temp_free(t0); +} + +// 0 or 2: !FCC0 +static inline void gen_op_eval_fbge(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    tcg_gen_xori_tl(dst, dst, 0x1); +} + +// !1: !(FCC0 & !FCC1) +static inline void gen_op_eval_fbuge(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    gen_mov_reg_FCC1(t0, src, fcc_offset); +    tcg_gen_andc_tl(dst, dst, t0); +    tcg_gen_xori_tl(dst, dst, 0x1); +    tcg_temp_free(t0); +} + +// 0 or 1: !FCC1 +static inline void gen_op_eval_fble(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    gen_mov_reg_FCC1(dst, src, fcc_offset); +    tcg_gen_xori_tl(dst, dst, 0x1); +} + +// !2: !(!FCC0 & FCC1) +static inline void gen_op_eval_fbule(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    gen_mov_reg_FCC1(t0, src, fcc_offset); +    tcg_gen_andc_tl(dst, t0, dst); +    tcg_gen_xori_tl(dst, dst, 0x1); +    tcg_temp_free(t0); +} + +// !3: !(FCC0 & FCC1) +static inline void gen_op_eval_fbo(TCGv dst, TCGv src, +                                    unsigned int fcc_offset) +{ +    TCGv t0 = tcg_temp_new(); +    gen_mov_reg_FCC0(dst, src, fcc_offset); +    gen_mov_reg_FCC1(t0, src, fcc_offset); +    tcg_gen_and_tl(dst, dst, t0); +    tcg_gen_xori_tl(dst, dst, 0x1); +    tcg_temp_free(t0); +} + +static inline void gen_branch2(DisasContext *dc, target_ulong pc1, +                               target_ulong pc2, TCGv r_cond) +{ +    TCGLabel *l1 = gen_new_label(); + +    tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1); + +    gen_goto_tb(dc, 0, pc1, pc1 + 4); + +    gen_set_label(l1); +    gen_goto_tb(dc, 1, pc2, pc2 + 4); +} + +static inline void gen_branch_a(DisasContext *dc, target_ulong pc1, +                                target_ulong pc2, TCGv r_cond) +{ +    TCGLabel *l1 = gen_new_label(); + +    tcg_gen_brcondi_tl(TCG_COND_EQ, r_cond, 0, l1); + +    gen_goto_tb(dc, 0, pc2, pc1); + +    gen_set_label(l1); +    gen_goto_tb(dc, 1, pc2 + 4, pc2 + 8); +} + +static inline void gen_generic_branch(DisasContext *dc) +{ +    TCGv npc0 = tcg_const_tl(dc->jump_pc[0]); +    TCGv npc1 = tcg_const_tl(dc->jump_pc[1]); +    TCGv zero = tcg_const_tl(0); + +    tcg_gen_movcond_tl(TCG_COND_NE, cpu_npc, cpu_cond, zero, npc0, npc1); + +    tcg_temp_free(npc0); +    tcg_temp_free(npc1); +    tcg_temp_free(zero); +} + +/* call this function before using the condition register as it may +   have been set for a jump */ +static inline void flush_cond(DisasContext *dc) +{ +    if (dc->npc == JUMP_PC) { +        gen_generic_branch(dc); +        dc->npc = DYNAMIC_PC; +    } +} + +static inline void save_npc(DisasContext *dc) +{ +    if (dc->npc == JUMP_PC) { +        gen_generic_branch(dc); +        dc->npc = DYNAMIC_PC; +    } else if (dc->npc != DYNAMIC_PC) { +        tcg_gen_movi_tl(cpu_npc, dc->npc); +    } +} + +static inline void update_psr(DisasContext *dc) +{ +    if (dc->cc_op != CC_OP_FLAGS) { +        dc->cc_op = CC_OP_FLAGS; +        gen_helper_compute_psr(cpu_env); +    } +} + +static inline void save_state(DisasContext *dc) +{ +    tcg_gen_movi_tl(cpu_pc, dc->pc); +    save_npc(dc); +} + +static inline void gen_mov_pc_npc(DisasContext *dc) +{ +    if (dc->npc == JUMP_PC) { +        gen_generic_branch(dc); +        tcg_gen_mov_tl(cpu_pc, cpu_npc); +        dc->pc = DYNAMIC_PC; +    } else if (dc->npc == DYNAMIC_PC) { +        tcg_gen_mov_tl(cpu_pc, cpu_npc); +        dc->pc = DYNAMIC_PC; +    } else { +        dc->pc = dc->npc; +    } +} + +static inline void gen_op_next_insn(void) +{ +    tcg_gen_mov_tl(cpu_pc, cpu_npc); +    tcg_gen_addi_tl(cpu_npc, cpu_npc, 4); +} + +static void free_compare(DisasCompare *cmp) +{ +    if (!cmp->g1) { +        tcg_temp_free(cmp->c1); +    } +    if (!cmp->g2) { +        tcg_temp_free(cmp->c2); +    } +} + +static void gen_compare(DisasCompare *cmp, bool xcc, unsigned int cond, +                        DisasContext *dc) +{ +    static int subcc_cond[16] = { +        TCG_COND_NEVER, +        TCG_COND_EQ, +        TCG_COND_LE, +        TCG_COND_LT, +        TCG_COND_LEU, +        TCG_COND_LTU, +        -1, /* neg */ +        -1, /* overflow */ +        TCG_COND_ALWAYS, +        TCG_COND_NE, +        TCG_COND_GT, +        TCG_COND_GE, +        TCG_COND_GTU, +        TCG_COND_GEU, +        -1, /* pos */ +        -1, /* no overflow */ +    }; + +    static int logic_cond[16] = { +        TCG_COND_NEVER, +        TCG_COND_EQ,     /* eq:  Z */ +        TCG_COND_LE,     /* le:  Z | (N ^ V) -> Z | N */ +        TCG_COND_LT,     /* lt:  N ^ V -> N */ +        TCG_COND_EQ,     /* leu: C | Z -> Z */ +        TCG_COND_NEVER,  /* ltu: C -> 0 */ +        TCG_COND_LT,     /* neg: N */ +        TCG_COND_NEVER,  /* vs:  V -> 0 */ +        TCG_COND_ALWAYS, +        TCG_COND_NE,     /* ne:  !Z */ +        TCG_COND_GT,     /* gt:  !(Z | (N ^ V)) -> !(Z | N) */ +        TCG_COND_GE,     /* ge:  !(N ^ V) -> !N */ +        TCG_COND_NE,     /* gtu: !(C | Z) -> !Z */ +        TCG_COND_ALWAYS, /* geu: !C -> 1 */ +        TCG_COND_GE,     /* pos: !N */ +        TCG_COND_ALWAYS, /* vc:  !V -> 1 */ +    }; + +    TCGv_i32 r_src; +    TCGv r_dst; + +#ifdef TARGET_SPARC64 +    if (xcc) { +        r_src = cpu_xcc; +    } else { +        r_src = cpu_psr; +    } +#else +    r_src = cpu_psr; +#endif + +    switch (dc->cc_op) { +    case CC_OP_LOGIC: +        cmp->cond = logic_cond[cond]; +    do_compare_dst_0: +        cmp->is_bool = false; +        cmp->g2 = false; +        cmp->c2 = tcg_const_tl(0); +#ifdef TARGET_SPARC64 +        if (!xcc) { +            cmp->g1 = false; +            cmp->c1 = tcg_temp_new(); +            tcg_gen_ext32s_tl(cmp->c1, cpu_cc_dst); +            break; +        } +#endif +        cmp->g1 = true; +        cmp->c1 = cpu_cc_dst; +        break; + +    case CC_OP_SUB: +        switch (cond) { +        case 6:  /* neg */ +        case 14: /* pos */ +            cmp->cond = (cond == 6 ? TCG_COND_LT : TCG_COND_GE); +            goto do_compare_dst_0; + +        case 7: /* overflow */ +        case 15: /* !overflow */ +            goto do_dynamic; + +        default: +            cmp->cond = subcc_cond[cond]; +            cmp->is_bool = false; +#ifdef TARGET_SPARC64 +            if (!xcc) { +                /* Note that sign-extension works for unsigned compares as +                   long as both operands are sign-extended.  */ +                cmp->g1 = cmp->g2 = false; +                cmp->c1 = tcg_temp_new(); +                cmp->c2 = tcg_temp_new(); +                tcg_gen_ext32s_tl(cmp->c1, cpu_cc_src); +                tcg_gen_ext32s_tl(cmp->c2, cpu_cc_src2); +                break; +            } +#endif +            cmp->g1 = cmp->g2 = true; +            cmp->c1 = cpu_cc_src; +            cmp->c2 = cpu_cc_src2; +            break; +        } +        break; + +    default: +    do_dynamic: +        gen_helper_compute_psr(cpu_env); +        dc->cc_op = CC_OP_FLAGS; +        /* FALLTHRU */ + +    case CC_OP_FLAGS: +        /* We're going to generate a boolean result.  */ +        cmp->cond = TCG_COND_NE; +        cmp->is_bool = true; +        cmp->g1 = cmp->g2 = false; +        cmp->c1 = r_dst = tcg_temp_new(); +        cmp->c2 = tcg_const_tl(0); + +        switch (cond) { +        case 0x0: +            gen_op_eval_bn(r_dst); +            break; +        case 0x1: +            gen_op_eval_be(r_dst, r_src); +            break; +        case 0x2: +            gen_op_eval_ble(r_dst, r_src); +            break; +        case 0x3: +            gen_op_eval_bl(r_dst, r_src); +            break; +        case 0x4: +            gen_op_eval_bleu(r_dst, r_src); +            break; +        case 0x5: +            gen_op_eval_bcs(r_dst, r_src); +            break; +        case 0x6: +            gen_op_eval_bneg(r_dst, r_src); +            break; +        case 0x7: +            gen_op_eval_bvs(r_dst, r_src); +            break; +        case 0x8: +            gen_op_eval_ba(r_dst); +            break; +        case 0x9: +            gen_op_eval_bne(r_dst, r_src); +            break; +        case 0xa: +            gen_op_eval_bg(r_dst, r_src); +            break; +        case 0xb: +            gen_op_eval_bge(r_dst, r_src); +            break; +        case 0xc: +            gen_op_eval_bgu(r_dst, r_src); +            break; +        case 0xd: +            gen_op_eval_bcc(r_dst, r_src); +            break; +        case 0xe: +            gen_op_eval_bpos(r_dst, r_src); +            break; +        case 0xf: +            gen_op_eval_bvc(r_dst, r_src); +            break; +        } +        break; +    } +} + +static void gen_fcompare(DisasCompare *cmp, unsigned int cc, unsigned int cond) +{ +    unsigned int offset; +    TCGv r_dst; + +    /* For now we still generate a straight boolean result.  */ +    cmp->cond = TCG_COND_NE; +    cmp->is_bool = true; +    cmp->g1 = cmp->g2 = false; +    cmp->c1 = r_dst = tcg_temp_new(); +    cmp->c2 = tcg_const_tl(0); + +    switch (cc) { +    default: +    case 0x0: +        offset = 0; +        break; +    case 0x1: +        offset = 32 - 10; +        break; +    case 0x2: +        offset = 34 - 10; +        break; +    case 0x3: +        offset = 36 - 10; +        break; +    } + +    switch (cond) { +    case 0x0: +        gen_op_eval_bn(r_dst); +        break; +    case 0x1: +        gen_op_eval_fbne(r_dst, cpu_fsr, offset); +        break; +    case 0x2: +        gen_op_eval_fblg(r_dst, cpu_fsr, offset); +        break; +    case 0x3: +        gen_op_eval_fbul(r_dst, cpu_fsr, offset); +        break; +    case 0x4: +        gen_op_eval_fbl(r_dst, cpu_fsr, offset); +        break; +    case 0x5: +        gen_op_eval_fbug(r_dst, cpu_fsr, offset); +        break; +    case 0x6: +        gen_op_eval_fbg(r_dst, cpu_fsr, offset); +        break; +    case 0x7: +        gen_op_eval_fbu(r_dst, cpu_fsr, offset); +        break; +    case 0x8: +        gen_op_eval_ba(r_dst); +        break; +    case 0x9: +        gen_op_eval_fbe(r_dst, cpu_fsr, offset); +        break; +    case 0xa: +        gen_op_eval_fbue(r_dst, cpu_fsr, offset); +        break; +    case 0xb: +        gen_op_eval_fbge(r_dst, cpu_fsr, offset); +        break; +    case 0xc: +        gen_op_eval_fbuge(r_dst, cpu_fsr, offset); +        break; +    case 0xd: +        gen_op_eval_fble(r_dst, cpu_fsr, offset); +        break; +    case 0xe: +        gen_op_eval_fbule(r_dst, cpu_fsr, offset); +        break; +    case 0xf: +        gen_op_eval_fbo(r_dst, cpu_fsr, offset); +        break; +    } +} + +static void gen_cond(TCGv r_dst, unsigned int cc, unsigned int cond, +                     DisasContext *dc) +{ +    DisasCompare cmp; +    gen_compare(&cmp, cc, cond, dc); + +    /* The interface is to return a boolean in r_dst.  */ +    if (cmp.is_bool) { +        tcg_gen_mov_tl(r_dst, cmp.c1); +    } else { +        tcg_gen_setcond_tl(cmp.cond, r_dst, cmp.c1, cmp.c2); +    } + +    free_compare(&cmp); +} + +static void gen_fcond(TCGv r_dst, unsigned int cc, unsigned int cond) +{ +    DisasCompare cmp; +    gen_fcompare(&cmp, cc, cond); + +    /* The interface is to return a boolean in r_dst.  */ +    if (cmp.is_bool) { +        tcg_gen_mov_tl(r_dst, cmp.c1); +    } else { +        tcg_gen_setcond_tl(cmp.cond, r_dst, cmp.c1, cmp.c2); +    } + +    free_compare(&cmp); +} + +#ifdef TARGET_SPARC64 +// Inverted logic +static const int gen_tcg_cond_reg[8] = { +    -1, +    TCG_COND_NE, +    TCG_COND_GT, +    TCG_COND_GE, +    -1, +    TCG_COND_EQ, +    TCG_COND_LE, +    TCG_COND_LT, +}; + +static void gen_compare_reg(DisasCompare *cmp, int cond, TCGv r_src) +{ +    cmp->cond = tcg_invert_cond(gen_tcg_cond_reg[cond]); +    cmp->is_bool = false; +    cmp->g1 = true; +    cmp->g2 = false; +    cmp->c1 = r_src; +    cmp->c2 = tcg_const_tl(0); +} + +static inline void gen_cond_reg(TCGv r_dst, int cond, TCGv r_src) +{ +    DisasCompare cmp; +    gen_compare_reg(&cmp, cond, r_src); + +    /* The interface is to return a boolean in r_dst.  */ +    tcg_gen_setcond_tl(cmp.cond, r_dst, cmp.c1, cmp.c2); + +    free_compare(&cmp); +} +#endif + +static void do_branch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) +{ +    unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); +    target_ulong target = dc->pc + offset; + +#ifdef TARGET_SPARC64 +    if (unlikely(AM_CHECK(dc))) { +        target &= 0xffffffffULL; +    } +#endif +    if (cond == 0x0) { +        /* unconditional not taken */ +        if (a) { +            dc->pc = dc->npc + 4; +            dc->npc = dc->pc + 4; +        } else { +            dc->pc = dc->npc; +            dc->npc = dc->pc + 4; +        } +    } else if (cond == 0x8) { +        /* unconditional taken */ +        if (a) { +            dc->pc = target; +            dc->npc = dc->pc + 4; +        } else { +            dc->pc = dc->npc; +            dc->npc = target; +            tcg_gen_mov_tl(cpu_pc, cpu_npc); +        } +    } else { +        flush_cond(dc); +        gen_cond(cpu_cond, cc, cond, dc); +        if (a) { +            gen_branch_a(dc, target, dc->npc, cpu_cond); +            dc->is_br = 1; +        } else { +            dc->pc = dc->npc; +            dc->jump_pc[0] = target; +            if (unlikely(dc->npc == DYNAMIC_PC)) { +                dc->jump_pc[1] = DYNAMIC_PC; +                tcg_gen_addi_tl(cpu_pc, cpu_npc, 4); +            } else { +                dc->jump_pc[1] = dc->npc + 4; +                dc->npc = JUMP_PC; +            } +        } +    } +} + +static void do_fbranch(DisasContext *dc, int32_t offset, uint32_t insn, int cc) +{ +    unsigned int cond = GET_FIELD(insn, 3, 6), a = (insn & (1 << 29)); +    target_ulong target = dc->pc + offset; + +#ifdef TARGET_SPARC64 +    if (unlikely(AM_CHECK(dc))) { +        target &= 0xffffffffULL; +    } +#endif +    if (cond == 0x0) { +        /* unconditional not taken */ +        if (a) { +            dc->pc = dc->npc + 4; +            dc->npc = dc->pc + 4; +        } else { +            dc->pc = dc->npc; +            dc->npc = dc->pc + 4; +        } +    } else if (cond == 0x8) { +        /* unconditional taken */ +        if (a) { +            dc->pc = target; +            dc->npc = dc->pc + 4; +        } else { +            dc->pc = dc->npc; +            dc->npc = target; +            tcg_gen_mov_tl(cpu_pc, cpu_npc); +        } +    } else { +        flush_cond(dc); +        gen_fcond(cpu_cond, cc, cond); +        if (a) { +            gen_branch_a(dc, target, dc->npc, cpu_cond); +            dc->is_br = 1; +        } else { +            dc->pc = dc->npc; +            dc->jump_pc[0] = target; +            if (unlikely(dc->npc == DYNAMIC_PC)) { +                dc->jump_pc[1] = DYNAMIC_PC; +                tcg_gen_addi_tl(cpu_pc, cpu_npc, 4); +            } else { +                dc->jump_pc[1] = dc->npc + 4; +                dc->npc = JUMP_PC; +            } +        } +    } +} + +#ifdef TARGET_SPARC64 +static void do_branch_reg(DisasContext *dc, int32_t offset, uint32_t insn, +                          TCGv r_reg) +{ +    unsigned int cond = GET_FIELD_SP(insn, 25, 27), a = (insn & (1 << 29)); +    target_ulong target = dc->pc + offset; + +    if (unlikely(AM_CHECK(dc))) { +        target &= 0xffffffffULL; +    } +    flush_cond(dc); +    gen_cond_reg(cpu_cond, cond, r_reg); +    if (a) { +        gen_branch_a(dc, target, dc->npc, cpu_cond); +        dc->is_br = 1; +    } else { +        dc->pc = dc->npc; +        dc->jump_pc[0] = target; +        if (unlikely(dc->npc == DYNAMIC_PC)) { +            dc->jump_pc[1] = DYNAMIC_PC; +            tcg_gen_addi_tl(cpu_pc, cpu_npc, 4); +        } else { +            dc->jump_pc[1] = dc->npc + 4; +            dc->npc = JUMP_PC; +        } +    } +} + +static inline void gen_op_fcmps(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2) +{ +    switch (fccno) { +    case 0: +        gen_helper_fcmps(cpu_env, r_rs1, r_rs2); +        break; +    case 1: +        gen_helper_fcmps_fcc1(cpu_env, r_rs1, r_rs2); +        break; +    case 2: +        gen_helper_fcmps_fcc2(cpu_env, r_rs1, r_rs2); +        break; +    case 3: +        gen_helper_fcmps_fcc3(cpu_env, r_rs1, r_rs2); +        break; +    } +} + +static inline void gen_op_fcmpd(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +{ +    switch (fccno) { +    case 0: +        gen_helper_fcmpd(cpu_env, r_rs1, r_rs2); +        break; +    case 1: +        gen_helper_fcmpd_fcc1(cpu_env, r_rs1, r_rs2); +        break; +    case 2: +        gen_helper_fcmpd_fcc2(cpu_env, r_rs1, r_rs2); +        break; +    case 3: +        gen_helper_fcmpd_fcc3(cpu_env, r_rs1, r_rs2); +        break; +    } +} + +static inline void gen_op_fcmpq(int fccno) +{ +    switch (fccno) { +    case 0: +        gen_helper_fcmpq(cpu_env); +        break; +    case 1: +        gen_helper_fcmpq_fcc1(cpu_env); +        break; +    case 2: +        gen_helper_fcmpq_fcc2(cpu_env); +        break; +    case 3: +        gen_helper_fcmpq_fcc3(cpu_env); +        break; +    } +} + +static inline void gen_op_fcmpes(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2) +{ +    switch (fccno) { +    case 0: +        gen_helper_fcmpes(cpu_env, r_rs1, r_rs2); +        break; +    case 1: +        gen_helper_fcmpes_fcc1(cpu_env, r_rs1, r_rs2); +        break; +    case 2: +        gen_helper_fcmpes_fcc2(cpu_env, r_rs1, r_rs2); +        break; +    case 3: +        gen_helper_fcmpes_fcc3(cpu_env, r_rs1, r_rs2); +        break; +    } +} + +static inline void gen_op_fcmped(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +{ +    switch (fccno) { +    case 0: +        gen_helper_fcmped(cpu_env, r_rs1, r_rs2); +        break; +    case 1: +        gen_helper_fcmped_fcc1(cpu_env, r_rs1, r_rs2); +        break; +    case 2: +        gen_helper_fcmped_fcc2(cpu_env, r_rs1, r_rs2); +        break; +    case 3: +        gen_helper_fcmped_fcc3(cpu_env, r_rs1, r_rs2); +        break; +    } +} + +static inline void gen_op_fcmpeq(int fccno) +{ +    switch (fccno) { +    case 0: +        gen_helper_fcmpeq(cpu_env); +        break; +    case 1: +        gen_helper_fcmpeq_fcc1(cpu_env); +        break; +    case 2: +        gen_helper_fcmpeq_fcc2(cpu_env); +        break; +    case 3: +        gen_helper_fcmpeq_fcc3(cpu_env); +        break; +    } +} + +#else + +static inline void gen_op_fcmps(int fccno, TCGv r_rs1, TCGv r_rs2) +{ +    gen_helper_fcmps(cpu_env, r_rs1, r_rs2); +} + +static inline void gen_op_fcmpd(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +{ +    gen_helper_fcmpd(cpu_env, r_rs1, r_rs2); +} + +static inline void gen_op_fcmpq(int fccno) +{ +    gen_helper_fcmpq(cpu_env); +} + +static inline void gen_op_fcmpes(int fccno, TCGv r_rs1, TCGv r_rs2) +{ +    gen_helper_fcmpes(cpu_env, r_rs1, r_rs2); +} + +static inline void gen_op_fcmped(int fccno, TCGv_i64 r_rs1, TCGv_i64 r_rs2) +{ +    gen_helper_fcmped(cpu_env, r_rs1, r_rs2); +} + +static inline void gen_op_fcmpeq(int fccno) +{ +    gen_helper_fcmpeq(cpu_env); +} +#endif + +static inline void gen_op_fpexception_im(int fsr_flags) +{ +    TCGv_i32 r_const; + +    tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_NMASK); +    tcg_gen_ori_tl(cpu_fsr, cpu_fsr, fsr_flags); +    r_const = tcg_const_i32(TT_FP_EXCP); +    gen_helper_raise_exception(cpu_env, r_const); +    tcg_temp_free_i32(r_const); +} + +static int gen_trap_ifnofpu(DisasContext *dc) +{ +#if !defined(CONFIG_USER_ONLY) +    if (!dc->fpu_enabled) { +        TCGv_i32 r_const; + +        save_state(dc); +        r_const = tcg_const_i32(TT_NFPU_INSN); +        gen_helper_raise_exception(cpu_env, r_const); +        tcg_temp_free_i32(r_const); +        dc->is_br = 1; +        return 1; +    } +#endif +    return 0; +} + +static inline void gen_op_clear_ieee_excp_and_FTT(void) +{ +    tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_CEXC_NMASK); +} + +static inline void gen_fop_FF(DisasContext *dc, int rd, int rs, +                              void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32)) +{ +    TCGv_i32 dst, src; + +    src = gen_load_fpr_F(dc, rs); +    dst = gen_dest_fpr_F(dc); + +    gen(dst, cpu_env, src); + +    gen_store_fpr_F(dc, rd, dst); +} + +static inline void gen_ne_fop_FF(DisasContext *dc, int rd, int rs, +                                 void (*gen)(TCGv_i32, TCGv_i32)) +{ +    TCGv_i32 dst, src; + +    src = gen_load_fpr_F(dc, rs); +    dst = gen_dest_fpr_F(dc); + +    gen(dst, src); + +    gen_store_fpr_F(dc, rd, dst); +} + +static inline void gen_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, +                        void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32)) +{ +    TCGv_i32 dst, src1, src2; + +    src1 = gen_load_fpr_F(dc, rs1); +    src2 = gen_load_fpr_F(dc, rs2); +    dst = gen_dest_fpr_F(dc); + +    gen(dst, cpu_env, src1, src2); + +    gen_store_fpr_F(dc, rd, dst); +} + +#ifdef TARGET_SPARC64 +static inline void gen_ne_fop_FFF(DisasContext *dc, int rd, int rs1, int rs2, +                                  void (*gen)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ +    TCGv_i32 dst, src1, src2; + +    src1 = gen_load_fpr_F(dc, rs1); +    src2 = gen_load_fpr_F(dc, rs2); +    dst = gen_dest_fpr_F(dc); + +    gen(dst, src1, src2); + +    gen_store_fpr_F(dc, rd, dst); +} +#endif + +static inline void gen_fop_DD(DisasContext *dc, int rd, int rs, +                              void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64)) +{ +    TCGv_i64 dst, src; + +    src = gen_load_fpr_D(dc, rs); +    dst = gen_dest_fpr_D(dc, rd); + +    gen(dst, cpu_env, src); + +    gen_store_fpr_D(dc, rd, dst); +} + +#ifdef TARGET_SPARC64 +static inline void gen_ne_fop_DD(DisasContext *dc, int rd, int rs, +                                 void (*gen)(TCGv_i64, TCGv_i64)) +{ +    TCGv_i64 dst, src; + +    src = gen_load_fpr_D(dc, rs); +    dst = gen_dest_fpr_D(dc, rd); + +    gen(dst, src); + +    gen_store_fpr_D(dc, rd, dst); +} +#endif + +static inline void gen_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, +                        void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i64, TCGv_i64)) +{ +    TCGv_i64 dst, src1, src2; + +    src1 = gen_load_fpr_D(dc, rs1); +    src2 = gen_load_fpr_D(dc, rs2); +    dst = gen_dest_fpr_D(dc, rd); + +    gen(dst, cpu_env, src1, src2); + +    gen_store_fpr_D(dc, rd, dst); +} + +#ifdef TARGET_SPARC64 +static inline void gen_ne_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, +                                  void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ +    TCGv_i64 dst, src1, src2; + +    src1 = gen_load_fpr_D(dc, rs1); +    src2 = gen_load_fpr_D(dc, rs2); +    dst = gen_dest_fpr_D(dc, rd); + +    gen(dst, src1, src2); + +    gen_store_fpr_D(dc, rd, dst); +} + +static inline void gen_gsr_fop_DDD(DisasContext *dc, int rd, int rs1, int rs2, +                           void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ +    TCGv_i64 dst, src1, src2; + +    src1 = gen_load_fpr_D(dc, rs1); +    src2 = gen_load_fpr_D(dc, rs2); +    dst = gen_dest_fpr_D(dc, rd); + +    gen(dst, cpu_gsr, src1, src2); + +    gen_store_fpr_D(dc, rd, dst); +} + +static inline void gen_ne_fop_DDDD(DisasContext *dc, int rd, int rs1, int rs2, +                           void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) +{ +    TCGv_i64 dst, src0, src1, src2; + +    src1 = gen_load_fpr_D(dc, rs1); +    src2 = gen_load_fpr_D(dc, rs2); +    src0 = gen_load_fpr_D(dc, rd); +    dst = gen_dest_fpr_D(dc, rd); + +    gen(dst, src0, src1, src2); + +    gen_store_fpr_D(dc, rd, dst); +} +#endif + +static inline void gen_fop_QQ(DisasContext *dc, int rd, int rs, +                              void (*gen)(TCGv_ptr)) +{ +    gen_op_load_fpr_QT1(QFPREG(rs)); + +    gen(cpu_env); + +    gen_op_store_QT0_fpr(QFPREG(rd)); +    gen_update_fprs_dirty(QFPREG(rd)); +} + +#ifdef TARGET_SPARC64 +static inline void gen_ne_fop_QQ(DisasContext *dc, int rd, int rs, +                                 void (*gen)(TCGv_ptr)) +{ +    gen_op_load_fpr_QT1(QFPREG(rs)); + +    gen(cpu_env); + +    gen_op_store_QT0_fpr(QFPREG(rd)); +    gen_update_fprs_dirty(QFPREG(rd)); +} +#endif + +static inline void gen_fop_QQQ(DisasContext *dc, int rd, int rs1, int rs2, +                               void (*gen)(TCGv_ptr)) +{ +    gen_op_load_fpr_QT0(QFPREG(rs1)); +    gen_op_load_fpr_QT1(QFPREG(rs2)); + +    gen(cpu_env); + +    gen_op_store_QT0_fpr(QFPREG(rd)); +    gen_update_fprs_dirty(QFPREG(rd)); +} + +static inline void gen_fop_DFF(DisasContext *dc, int rd, int rs1, int rs2, +                        void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32, TCGv_i32)) +{ +    TCGv_i64 dst; +    TCGv_i32 src1, src2; + +    src1 = gen_load_fpr_F(dc, rs1); +    src2 = gen_load_fpr_F(dc, rs2); +    dst = gen_dest_fpr_D(dc, rd); + +    gen(dst, cpu_env, src1, src2); + +    gen_store_fpr_D(dc, rd, dst); +} + +static inline void gen_fop_QDD(DisasContext *dc, int rd, int rs1, int rs2, +                               void (*gen)(TCGv_ptr, TCGv_i64, TCGv_i64)) +{ +    TCGv_i64 src1, src2; + +    src1 = gen_load_fpr_D(dc, rs1); +    src2 = gen_load_fpr_D(dc, rs2); + +    gen(cpu_env, src1, src2); + +    gen_op_store_QT0_fpr(QFPREG(rd)); +    gen_update_fprs_dirty(QFPREG(rd)); +} + +#ifdef TARGET_SPARC64 +static inline void gen_fop_DF(DisasContext *dc, int rd, int rs, +                              void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32)) +{ +    TCGv_i64 dst; +    TCGv_i32 src; + +    src = gen_load_fpr_F(dc, rs); +    dst = gen_dest_fpr_D(dc, rd); + +    gen(dst, cpu_env, src); + +    gen_store_fpr_D(dc, rd, dst); +} +#endif + +static inline void gen_ne_fop_DF(DisasContext *dc, int rd, int rs, +                                 void (*gen)(TCGv_i64, TCGv_ptr, TCGv_i32)) +{ +    TCGv_i64 dst; +    TCGv_i32 src; + +    src = gen_load_fpr_F(dc, rs); +    dst = gen_dest_fpr_D(dc, rd); + +    gen(dst, cpu_env, src); + +    gen_store_fpr_D(dc, rd, dst); +} + +static inline void gen_fop_FD(DisasContext *dc, int rd, int rs, +                              void (*gen)(TCGv_i32, TCGv_ptr, TCGv_i64)) +{ +    TCGv_i32 dst; +    TCGv_i64 src; + +    src = gen_load_fpr_D(dc, rs); +    dst = gen_dest_fpr_F(dc); + +    gen(dst, cpu_env, src); + +    gen_store_fpr_F(dc, rd, dst); +} + +static inline void gen_fop_FQ(DisasContext *dc, int rd, int rs, +                              void (*gen)(TCGv_i32, TCGv_ptr)) +{ +    TCGv_i32 dst; + +    gen_op_load_fpr_QT1(QFPREG(rs)); +    dst = gen_dest_fpr_F(dc); + +    gen(dst, cpu_env); + +    gen_store_fpr_F(dc, rd, dst); +} + +static inline void gen_fop_DQ(DisasContext *dc, int rd, int rs, +                              void (*gen)(TCGv_i64, TCGv_ptr)) +{ +    TCGv_i64 dst; + +    gen_op_load_fpr_QT1(QFPREG(rs)); +    dst = gen_dest_fpr_D(dc, rd); + +    gen(dst, cpu_env); + +    gen_store_fpr_D(dc, rd, dst); +} + +static inline void gen_ne_fop_QF(DisasContext *dc, int rd, int rs, +                                 void (*gen)(TCGv_ptr, TCGv_i32)) +{ +    TCGv_i32 src; + +    src = gen_load_fpr_F(dc, rs); + +    gen(cpu_env, src); + +    gen_op_store_QT0_fpr(QFPREG(rd)); +    gen_update_fprs_dirty(QFPREG(rd)); +} + +static inline void gen_ne_fop_QD(DisasContext *dc, int rd, int rs, +                                 void (*gen)(TCGv_ptr, TCGv_i64)) +{ +    TCGv_i64 src; + +    src = gen_load_fpr_D(dc, rs); + +    gen(cpu_env, src); + +    gen_op_store_QT0_fpr(QFPREG(rd)); +    gen_update_fprs_dirty(QFPREG(rd)); +} + +/* asi moves */ +#ifdef TARGET_SPARC64 +static inline TCGv_i32 gen_get_asi(int insn, TCGv r_addr) +{ +    int asi; +    TCGv_i32 r_asi; + +    if (IS_IMM) { +        r_asi = tcg_temp_new_i32(); +        tcg_gen_mov_i32(r_asi, cpu_asi); +    } else { +        asi = GET_FIELD(insn, 19, 26); +        r_asi = tcg_const_i32(asi); +    } +    return r_asi; +} + +static inline void gen_ld_asi(TCGv dst, TCGv addr, int insn, int size, +                              int sign) +{ +    TCGv_i32 r_asi, r_size, r_sign; + +    r_asi = gen_get_asi(insn, addr); +    r_size = tcg_const_i32(size); +    r_sign = tcg_const_i32(sign); +    gen_helper_ld_asi(dst, cpu_env, addr, r_asi, r_size, r_sign); +    tcg_temp_free_i32(r_sign); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +} + +static inline void gen_st_asi(TCGv src, TCGv addr, int insn, int size) +{ +    TCGv_i32 r_asi, r_size; + +    r_asi = gen_get_asi(insn, addr); +    r_size = tcg_const_i32(size); +    gen_helper_st_asi(cpu_env, addr, src, r_asi, r_size); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +} + +static inline void gen_ldf_asi(TCGv addr, int insn, int size, int rd) +{ +    TCGv_i32 r_asi, r_size, r_rd; + +    r_asi = gen_get_asi(insn, addr); +    r_size = tcg_const_i32(size); +    r_rd = tcg_const_i32(rd); +    gen_helper_ldf_asi(cpu_env, addr, r_asi, r_size, r_rd); +    tcg_temp_free_i32(r_rd); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +} + +static inline void gen_stf_asi(TCGv addr, int insn, int size, int rd) +{ +    TCGv_i32 r_asi, r_size, r_rd; + +    r_asi = gen_get_asi(insn, addr); +    r_size = tcg_const_i32(size); +    r_rd = tcg_const_i32(rd); +    gen_helper_stf_asi(cpu_env, addr, r_asi, r_size, r_rd); +    tcg_temp_free_i32(r_rd); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +} + +static inline void gen_swap_asi(TCGv dst, TCGv src, TCGv addr, int insn) +{ +    TCGv_i32 r_asi, r_size, r_sign; +    TCGv_i64 t64 = tcg_temp_new_i64(); + +    r_asi = gen_get_asi(insn, addr); +    r_size = tcg_const_i32(4); +    r_sign = tcg_const_i32(0); +    gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_size, r_sign); +    tcg_temp_free_i32(r_sign); +    gen_helper_st_asi(cpu_env, addr, src, r_asi, r_size); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +    tcg_gen_trunc_i64_tl(dst, t64); +    tcg_temp_free_i64(t64); +} + +static inline void gen_ldda_asi(DisasContext *dc, TCGv hi, TCGv addr, +                                int insn, int rd) +{ +    TCGv_i32 r_asi, r_rd; + +    r_asi = gen_get_asi(insn, addr); +    r_rd = tcg_const_i32(rd); +    gen_helper_ldda_asi(cpu_env, addr, r_asi, r_rd); +    tcg_temp_free_i32(r_rd); +    tcg_temp_free_i32(r_asi); +} + +static inline void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, +                                int insn, int rd) +{ +    TCGv_i32 r_asi, r_size; +    TCGv lo = gen_load_gpr(dc, rd + 1); +    TCGv_i64 t64 = tcg_temp_new_i64(); + +    tcg_gen_concat_tl_i64(t64, lo, hi); +    r_asi = gen_get_asi(insn, addr); +    r_size = tcg_const_i32(8); +    gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_size); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +    tcg_temp_free_i64(t64); +} + +static inline void gen_casx_asi(DisasContext *dc, TCGv addr, +                                TCGv val2, int insn, int rd) +{ +    TCGv val1 = gen_load_gpr(dc, rd); +    TCGv dst = gen_dest_gpr(dc, rd); +    TCGv_i32 r_asi = gen_get_asi(insn, addr); + +    gen_helper_casx_asi(dst, cpu_env, addr, val1, val2, r_asi); +    tcg_temp_free_i32(r_asi); +    gen_store_gpr(dc, rd, dst); +} + +#elif !defined(CONFIG_USER_ONLY) + +static inline void gen_ld_asi(TCGv dst, TCGv addr, int insn, int size, +                              int sign) +{ +    TCGv_i32 r_asi, r_size, r_sign; +    TCGv_i64 t64 = tcg_temp_new_i64(); + +    r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26)); +    r_size = tcg_const_i32(size); +    r_sign = tcg_const_i32(sign); +    gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_size, r_sign); +    tcg_temp_free_i32(r_sign); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +    tcg_gen_trunc_i64_tl(dst, t64); +    tcg_temp_free_i64(t64); +} + +static inline void gen_st_asi(TCGv src, TCGv addr, int insn, int size) +{ +    TCGv_i32 r_asi, r_size; +    TCGv_i64 t64 = tcg_temp_new_i64(); + +    tcg_gen_extu_tl_i64(t64, src); +    r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26)); +    r_size = tcg_const_i32(size); +    gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_size); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +    tcg_temp_free_i64(t64); +} + +static inline void gen_swap_asi(TCGv dst, TCGv src, TCGv addr, int insn) +{ +    TCGv_i32 r_asi, r_size, r_sign; +    TCGv_i64 r_val, t64; + +    r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26)); +    r_size = tcg_const_i32(4); +    r_sign = tcg_const_i32(0); +    t64 = tcg_temp_new_i64(); +    gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_size, r_sign); +    tcg_temp_free(r_sign); +    r_val = tcg_temp_new_i64(); +    tcg_gen_extu_tl_i64(r_val, src); +    gen_helper_st_asi(cpu_env, addr, r_val, r_asi, r_size); +    tcg_temp_free_i64(r_val); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +    tcg_gen_trunc_i64_tl(dst, t64); +    tcg_temp_free_i64(t64); +} + +static inline void gen_ldda_asi(DisasContext *dc, TCGv hi, TCGv addr, +                                int insn, int rd) +{ +    TCGv_i32 r_asi, r_size, r_sign; +    TCGv t; +    TCGv_i64 t64; + +    r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26)); +    r_size = tcg_const_i32(8); +    r_sign = tcg_const_i32(0); +    t64 = tcg_temp_new_i64(); +    gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_size, r_sign); +    tcg_temp_free_i32(r_sign); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); + +    t = gen_dest_gpr(dc, rd + 1); +    tcg_gen_trunc_i64_tl(t, t64); +    gen_store_gpr(dc, rd + 1, t); + +    tcg_gen_shri_i64(t64, t64, 32); +    tcg_gen_trunc_i64_tl(hi, t64); +    tcg_temp_free_i64(t64); +    gen_store_gpr(dc, rd, hi); +} + +static inline void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr, +                                int insn, int rd) +{ +    TCGv_i32 r_asi, r_size; +    TCGv lo = gen_load_gpr(dc, rd + 1); +    TCGv_i64 t64 = tcg_temp_new_i64(); + +    tcg_gen_concat_tl_i64(t64, lo, hi); +    r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26)); +    r_size = tcg_const_i32(8); +    gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_size); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +    tcg_temp_free_i64(t64); +} +#endif + +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) +static inline void gen_cas_asi(DisasContext *dc, TCGv addr, +                               TCGv val2, int insn, int rd) +{ +    TCGv val1 = gen_load_gpr(dc, rd); +    TCGv dst = gen_dest_gpr(dc, rd); +#ifdef TARGET_SPARC64 +    TCGv_i32 r_asi = gen_get_asi(insn, addr); +#else +    TCGv_i32 r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26)); +#endif + +    gen_helper_cas_asi(dst, cpu_env, addr, val1, val2, r_asi); +    tcg_temp_free_i32(r_asi); +    gen_store_gpr(dc, rd, dst); +} + +static inline void gen_ldstub_asi(TCGv dst, TCGv addr, int insn) +{ +    TCGv_i64 r_val; +    TCGv_i32 r_asi, r_size; + +    gen_ld_asi(dst, addr, insn, 1, 0); + +    r_val = tcg_const_i64(0xffULL); +    r_asi = tcg_const_i32(GET_FIELD(insn, 19, 26)); +    r_size = tcg_const_i32(1); +    gen_helper_st_asi(cpu_env, addr, r_val, r_asi, r_size); +    tcg_temp_free_i32(r_size); +    tcg_temp_free_i32(r_asi); +    tcg_temp_free_i64(r_val); +} +#endif + +static TCGv get_src1(DisasContext *dc, unsigned int insn) +{ +    unsigned int rs1 = GET_FIELD(insn, 13, 17); +    return gen_load_gpr(dc, rs1); +} + +static TCGv get_src2(DisasContext *dc, unsigned int insn) +{ +    if (IS_IMM) { /* immediate */ +        target_long simm = GET_FIELDs(insn, 19, 31); +        TCGv t = get_temp_tl(dc); +        tcg_gen_movi_tl(t, simm); +        return t; +    } else {      /* register */ +        unsigned int rs2 = GET_FIELD(insn, 27, 31); +        return gen_load_gpr(dc, rs2); +    } +} + +#ifdef TARGET_SPARC64 +static void gen_fmovs(DisasContext *dc, DisasCompare *cmp, int rd, int rs) +{ +    TCGv_i32 c32, zero, dst, s1, s2; + +    /* We have two choices here: extend the 32 bit data and use movcond_i64, +       or fold the comparison down to 32 bits and use movcond_i32.  Choose +       the later.  */ +    c32 = tcg_temp_new_i32(); +    if (cmp->is_bool) { +        tcg_gen_trunc_i64_i32(c32, cmp->c1); +    } else { +        TCGv_i64 c64 = tcg_temp_new_i64(); +        tcg_gen_setcond_i64(cmp->cond, c64, cmp->c1, cmp->c2); +        tcg_gen_trunc_i64_i32(c32, c64); +        tcg_temp_free_i64(c64); +    } + +    s1 = gen_load_fpr_F(dc, rs); +    s2 = gen_load_fpr_F(dc, rd); +    dst = gen_dest_fpr_F(dc); +    zero = tcg_const_i32(0); + +    tcg_gen_movcond_i32(TCG_COND_NE, dst, c32, zero, s1, s2); + +    tcg_temp_free_i32(c32); +    tcg_temp_free_i32(zero); +    gen_store_fpr_F(dc, rd, dst); +} + +static void gen_fmovd(DisasContext *dc, DisasCompare *cmp, int rd, int rs) +{ +    TCGv_i64 dst = gen_dest_fpr_D(dc, rd); +    tcg_gen_movcond_i64(cmp->cond, dst, cmp->c1, cmp->c2, +                        gen_load_fpr_D(dc, rs), +                        gen_load_fpr_D(dc, rd)); +    gen_store_fpr_D(dc, rd, dst); +} + +static void gen_fmovq(DisasContext *dc, DisasCompare *cmp, int rd, int rs) +{ +    int qd = QFPREG(rd); +    int qs = QFPREG(rs); + +    tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2], cmp->c1, cmp->c2, +                        cpu_fpr[qs / 2], cpu_fpr[qd / 2]); +    tcg_gen_movcond_i64(cmp->cond, cpu_fpr[qd / 2 + 1], cmp->c1, cmp->c2, +                        cpu_fpr[qs / 2 + 1], cpu_fpr[qd / 2 + 1]); + +    gen_update_fprs_dirty(qd); +} + +#ifndef CONFIG_USER_ONLY +static inline void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_ptr cpu_env) +{ +    TCGv_i32 r_tl = tcg_temp_new_i32(); + +    /* load env->tl into r_tl */ +    tcg_gen_ld_i32(r_tl, cpu_env, offsetof(CPUSPARCState, tl)); + +    /* tl = [0 ... MAXTL_MASK] where MAXTL_MASK must be power of 2 */ +    tcg_gen_andi_i32(r_tl, r_tl, MAXTL_MASK); + +    /* calculate offset to current trap state from env->ts, reuse r_tl */ +    tcg_gen_muli_i32(r_tl, r_tl, sizeof (trap_state)); +    tcg_gen_addi_ptr(r_tsptr, cpu_env, offsetof(CPUSPARCState, ts)); + +    /* tsptr = env->ts[env->tl & MAXTL_MASK] */ +    { +        TCGv_ptr r_tl_tmp = tcg_temp_new_ptr(); +        tcg_gen_ext_i32_ptr(r_tl_tmp, r_tl); +        tcg_gen_add_ptr(r_tsptr, r_tsptr, r_tl_tmp); +        tcg_temp_free_ptr(r_tl_tmp); +    } + +    tcg_temp_free_i32(r_tl); +} +#endif + +static void gen_edge(DisasContext *dc, TCGv dst, TCGv s1, TCGv s2, +                     int width, bool cc, bool left) +{ +    TCGv lo1, lo2, t1, t2; +    uint64_t amask, tabl, tabr; +    int shift, imask, omask; + +    if (cc) { +        tcg_gen_mov_tl(cpu_cc_src, s1); +        tcg_gen_mov_tl(cpu_cc_src2, s2); +        tcg_gen_sub_tl(cpu_cc_dst, s1, s2); +        tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB); +        dc->cc_op = CC_OP_SUB; +    } + +    /* Theory of operation: there are two tables, left and right (not to +       be confused with the left and right versions of the opcode).  These +       are indexed by the low 3 bits of the inputs.  To make things "easy", +       these tables are loaded into two constants, TABL and TABR below. +       The operation index = (input & imask) << shift calculates the index +       into the constant, while val = (table >> index) & omask calculates +       the value we're looking for.  */ +    switch (width) { +    case 8: +        imask = 0x7; +        shift = 3; +        omask = 0xff; +        if (left) { +            tabl = 0x80c0e0f0f8fcfeffULL; +            tabr = 0xff7f3f1f0f070301ULL; +        } else { +            tabl = 0x0103070f1f3f7fffULL; +            tabr = 0xfffefcf8f0e0c080ULL; +        } +        break; +    case 16: +        imask = 0x6; +        shift = 1; +        omask = 0xf; +        if (left) { +            tabl = 0x8cef; +            tabr = 0xf731; +        } else { +            tabl = 0x137f; +            tabr = 0xfec8; +        } +        break; +    case 32: +        imask = 0x4; +        shift = 0; +        omask = 0x3; +        if (left) { +            tabl = (2 << 2) | 3; +            tabr = (3 << 2) | 1; +        } else { +            tabl = (1 << 2) | 3; +            tabr = (3 << 2) | 2; +        } +        break; +    default: +        abort(); +    } + +    lo1 = tcg_temp_new(); +    lo2 = tcg_temp_new(); +    tcg_gen_andi_tl(lo1, s1, imask); +    tcg_gen_andi_tl(lo2, s2, imask); +    tcg_gen_shli_tl(lo1, lo1, shift); +    tcg_gen_shli_tl(lo2, lo2, shift); + +    t1 = tcg_const_tl(tabl); +    t2 = tcg_const_tl(tabr); +    tcg_gen_shr_tl(lo1, t1, lo1); +    tcg_gen_shr_tl(lo2, t2, lo2); +    tcg_gen_andi_tl(dst, lo1, omask); +    tcg_gen_andi_tl(lo2, lo2, omask); + +    amask = -8; +    if (AM_CHECK(dc)) { +        amask &= 0xffffffffULL; +    } +    tcg_gen_andi_tl(s1, s1, amask); +    tcg_gen_andi_tl(s2, s2, amask); + +    /* We want to compute +        dst = (s1 == s2 ? lo1 : lo1 & lo2). +       We've already done dst = lo1, so this reduces to +        dst &= (s1 == s2 ? -1 : lo2) +       Which we perform by +        lo2 |= -(s1 == s2) +        dst &= lo2 +    */ +    tcg_gen_setcond_tl(TCG_COND_EQ, t1, s1, s2); +    tcg_gen_neg_tl(t1, t1); +    tcg_gen_or_tl(lo2, lo2, t1); +    tcg_gen_and_tl(dst, dst, lo2); + +    tcg_temp_free(lo1); +    tcg_temp_free(lo2); +    tcg_temp_free(t1); +    tcg_temp_free(t2); +} + +static void gen_alignaddr(TCGv dst, TCGv s1, TCGv s2, bool left) +{ +    TCGv tmp = tcg_temp_new(); + +    tcg_gen_add_tl(tmp, s1, s2); +    tcg_gen_andi_tl(dst, tmp, -8); +    if (left) { +        tcg_gen_neg_tl(tmp, tmp); +    } +    tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, tmp, 0, 3); + +    tcg_temp_free(tmp); +} + +static void gen_faligndata(TCGv dst, TCGv gsr, TCGv s1, TCGv s2) +{ +    TCGv t1, t2, shift; + +    t1 = tcg_temp_new(); +    t2 = tcg_temp_new(); +    shift = tcg_temp_new(); + +    tcg_gen_andi_tl(shift, gsr, 7); +    tcg_gen_shli_tl(shift, shift, 3); +    tcg_gen_shl_tl(t1, s1, shift); + +    /* A shift of 64 does not produce 0 in TCG.  Divide this into a +       shift of (up to 63) followed by a constant shift of 1.  */ +    tcg_gen_xori_tl(shift, shift, 63); +    tcg_gen_shr_tl(t2, s2, shift); +    tcg_gen_shri_tl(t2, t2, 1); + +    tcg_gen_or_tl(dst, t1, t2); + +    tcg_temp_free(t1); +    tcg_temp_free(t2); +    tcg_temp_free(shift); +} +#endif + +#define CHECK_IU_FEATURE(dc, FEATURE)                      \ +    if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE))  \ +        goto illegal_insn; +#define CHECK_FPU_FEATURE(dc, FEATURE)                     \ +    if (!((dc)->def->features & CPU_FEATURE_ ## FEATURE))  \ +        goto nfpu_insn; + +/* before an instruction, dc->pc must be static */ +static void disas_sparc_insn(DisasContext * dc, unsigned int insn) +{ +    unsigned int opc, rs1, rs2, rd; +    TCGv cpu_src1, cpu_src2; +    TCGv_i32 cpu_src1_32, cpu_src2_32, cpu_dst_32; +    TCGv_i64 cpu_src1_64, cpu_src2_64, cpu_dst_64; +    target_long simm; + +    if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { +        tcg_gen_debug_insn_start(dc->pc); +    } + +    opc = GET_FIELD(insn, 0, 1); +    rd = GET_FIELD(insn, 2, 6); + +    switch (opc) { +    case 0:                     /* branches/sethi */ +        { +            unsigned int xop = GET_FIELD(insn, 7, 9); +            int32_t target; +            switch (xop) { +#ifdef TARGET_SPARC64 +            case 0x1:           /* V9 BPcc */ +                { +                    int cc; + +                    target = GET_FIELD_SP(insn, 0, 18); +                    target = sign_extend(target, 19); +                    target <<= 2; +                    cc = GET_FIELD_SP(insn, 20, 21); +                    if (cc == 0) +                        do_branch(dc, target, insn, 0); +                    else if (cc == 2) +                        do_branch(dc, target, insn, 1); +                    else +                        goto illegal_insn; +                    goto jmp_insn; +                } +            case 0x3:           /* V9 BPr */ +                { +                    target = GET_FIELD_SP(insn, 0, 13) | +                        (GET_FIELD_SP(insn, 20, 21) << 14); +                    target = sign_extend(target, 16); +                    target <<= 2; +                    cpu_src1 = get_src1(dc, insn); +                    do_branch_reg(dc, target, insn, cpu_src1); +                    goto jmp_insn; +                } +            case 0x5:           /* V9 FBPcc */ +                { +                    int cc = GET_FIELD_SP(insn, 20, 21); +                    if (gen_trap_ifnofpu(dc)) { +                        goto jmp_insn; +                    } +                    target = GET_FIELD_SP(insn, 0, 18); +                    target = sign_extend(target, 19); +                    target <<= 2; +                    do_fbranch(dc, target, insn, cc); +                    goto jmp_insn; +                } +#else +            case 0x7:           /* CBN+x */ +                { +                    goto ncp_insn; +                } +#endif +            case 0x2:           /* BN+x */ +                { +                    target = GET_FIELD(insn, 10, 31); +                    target = sign_extend(target, 22); +                    target <<= 2; +                    do_branch(dc, target, insn, 0); +                    goto jmp_insn; +                } +            case 0x6:           /* FBN+x */ +                { +                    if (gen_trap_ifnofpu(dc)) { +                        goto jmp_insn; +                    } +                    target = GET_FIELD(insn, 10, 31); +                    target = sign_extend(target, 22); +                    target <<= 2; +                    do_fbranch(dc, target, insn, 0); +                    goto jmp_insn; +                } +            case 0x4:           /* SETHI */ +                /* Special-case %g0 because that's the canonical nop.  */ +                if (rd) { +                    uint32_t value = GET_FIELD(insn, 10, 31); +                    TCGv t = gen_dest_gpr(dc, rd); +                    tcg_gen_movi_tl(t, value << 10); +                    gen_store_gpr(dc, rd, t); +                } +                break; +            case 0x0:           /* UNIMPL */ +            default: +                goto illegal_insn; +            } +            break; +        } +        break; +    case 1:                     /*CALL*/ +        { +            target_long target = GET_FIELDs(insn, 2, 31) << 2; +            TCGv o7 = gen_dest_gpr(dc, 15); + +            tcg_gen_movi_tl(o7, dc->pc); +            gen_store_gpr(dc, 15, o7); +            target += dc->pc; +            gen_mov_pc_npc(dc); +#ifdef TARGET_SPARC64 +            if (unlikely(AM_CHECK(dc))) { +                target &= 0xffffffffULL; +            } +#endif +            dc->npc = target; +        } +        goto jmp_insn; +    case 2:                     /* FPU & Logical Operations */ +        { +            unsigned int xop = GET_FIELD(insn, 7, 12); +            TCGv cpu_dst = get_temp_tl(dc); +            TCGv cpu_tmp0; + +            if (xop == 0x3a) {  /* generate trap */ +                int cond = GET_FIELD(insn, 3, 6); +                TCGv_i32 trap; +                TCGLabel *l1 = NULL; +                int mask; + +                if (cond == 0) { +                    /* Trap never.  */ +                    break; +                } + +                save_state(dc); + +                if (cond != 8) { +                    /* Conditional trap.  */ +                    DisasCompare cmp; +#ifdef TARGET_SPARC64 +                    /* V9 icc/xcc */ +                    int cc = GET_FIELD_SP(insn, 11, 12); +                    if (cc == 0) { +                        gen_compare(&cmp, 0, cond, dc); +                    } else if (cc == 2) { +                        gen_compare(&cmp, 1, cond, dc); +                    } else { +                        goto illegal_insn; +                    } +#else +                    gen_compare(&cmp, 0, cond, dc); +#endif +                    l1 = gen_new_label(); +                    tcg_gen_brcond_tl(tcg_invert_cond(cmp.cond), +                                      cmp.c1, cmp.c2, l1); +                    free_compare(&cmp); +                } + +                mask = ((dc->def->features & CPU_FEATURE_HYPV) && supervisor(dc) +                        ? UA2005_HTRAP_MASK : V8_TRAP_MASK); + +                /* Don't use the normal temporaries, as they may well have +                   gone out of scope with the branch above.  While we're +                   doing that we might as well pre-truncate to 32-bit.  */ +                trap = tcg_temp_new_i32(); + +                rs1 = GET_FIELD_SP(insn, 14, 18); +                if (IS_IMM) { +                    rs2 = GET_FIELD_SP(insn, 0, 6); +                    if (rs1 == 0) { +                        tcg_gen_movi_i32(trap, (rs2 & mask) + TT_TRAP); +                        /* Signal that the trap value is fully constant.  */ +                        mask = 0; +                    } else { +                        TCGv t1 = gen_load_gpr(dc, rs1); +                        tcg_gen_trunc_tl_i32(trap, t1); +                        tcg_gen_addi_i32(trap, trap, rs2); +                    } +                } else { +                    TCGv t1, t2; +                    rs2 = GET_FIELD_SP(insn, 0, 4); +                    t1 = gen_load_gpr(dc, rs1); +                    t2 = gen_load_gpr(dc, rs2); +                    tcg_gen_add_tl(t1, t1, t2); +                    tcg_gen_trunc_tl_i32(trap, t1); +                } +                if (mask != 0) { +                    tcg_gen_andi_i32(trap, trap, mask); +                    tcg_gen_addi_i32(trap, trap, TT_TRAP); +                } + +                gen_helper_raise_exception(cpu_env, trap); +                tcg_temp_free_i32(trap); + +                if (cond == 8) { +                    /* An unconditional trap ends the TB.  */ +                    dc->is_br = 1; +                    goto jmp_insn; +                } else { +                    /* A conditional trap falls through to the next insn.  */ +                    gen_set_label(l1); +                    break; +                } +            } else if (xop == 0x28) { +                rs1 = GET_FIELD(insn, 13, 17); +                switch(rs1) { +                case 0: /* rdy */ +#ifndef TARGET_SPARC64 +                case 0x01 ... 0x0e: /* undefined in the SPARCv8 +                                       manual, rdy on the microSPARC +                                       II */ +                case 0x0f:          /* stbar in the SPARCv8 manual, +                                       rdy on the microSPARC II */ +                case 0x10 ... 0x1f: /* implementation-dependent in the +                                       SPARCv8 manual, rdy on the +                                       microSPARC II */ +                    /* Read Asr17 */ +                    if (rs1 == 0x11 && dc->def->features & CPU_FEATURE_ASR17) { +                        TCGv t = gen_dest_gpr(dc, rd); +                        /* Read Asr17 for a Leon3 monoprocessor */ +                        tcg_gen_movi_tl(t, (1 << 8) | (dc->def->nwindows - 1)); +                        gen_store_gpr(dc, rd, t); +                        break; +                    } +#endif +                    gen_store_gpr(dc, rd, cpu_y); +                    break; +#ifdef TARGET_SPARC64 +                case 0x2: /* V9 rdccr */ +                    update_psr(dc); +                    gen_helper_rdccr(cpu_dst, cpu_env); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x3: /* V9 rdasi */ +                    tcg_gen_ext_i32_tl(cpu_dst, cpu_asi); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x4: /* V9 rdtick */ +                    { +                        TCGv_ptr r_tickptr; + +                        r_tickptr = tcg_temp_new_ptr(); +                        tcg_gen_ld_ptr(r_tickptr, cpu_env, +                                       offsetof(CPUSPARCState, tick)); +                        gen_helper_tick_get_count(cpu_dst, r_tickptr); +                        tcg_temp_free_ptr(r_tickptr); +                        gen_store_gpr(dc, rd, cpu_dst); +                    } +                    break; +                case 0x5: /* V9 rdpc */ +                    { +                        TCGv t = gen_dest_gpr(dc, rd); +                        if (unlikely(AM_CHECK(dc))) { +                            tcg_gen_movi_tl(t, dc->pc & 0xffffffffULL); +                        } else { +                            tcg_gen_movi_tl(t, dc->pc); +                        } +                        gen_store_gpr(dc, rd, t); +                    } +                    break; +                case 0x6: /* V9 rdfprs */ +                    tcg_gen_ext_i32_tl(cpu_dst, cpu_fprs); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0xf: /* V9 membar */ +                    break; /* no effect */ +                case 0x13: /* Graphics Status */ +                    if (gen_trap_ifnofpu(dc)) { +                        goto jmp_insn; +                    } +                    gen_store_gpr(dc, rd, cpu_gsr); +                    break; +                case 0x16: /* Softint */ +                    tcg_gen_ext_i32_tl(cpu_dst, cpu_softint); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x17: /* Tick compare */ +                    gen_store_gpr(dc, rd, cpu_tick_cmpr); +                    break; +                case 0x18: /* System tick */ +                    { +                        TCGv_ptr r_tickptr; + +                        r_tickptr = tcg_temp_new_ptr(); +                        tcg_gen_ld_ptr(r_tickptr, cpu_env, +                                       offsetof(CPUSPARCState, stick)); +                        gen_helper_tick_get_count(cpu_dst, r_tickptr); +                        tcg_temp_free_ptr(r_tickptr); +                        gen_store_gpr(dc, rd, cpu_dst); +                    } +                    break; +                case 0x19: /* System tick compare */ +                    gen_store_gpr(dc, rd, cpu_stick_cmpr); +                    break; +                case 0x10: /* Performance Control */ +                case 0x11: /* Performance Instrumentation Counter */ +                case 0x12: /* Dispatch Control */ +                case 0x14: /* Softint set, WO */ +                case 0x15: /* Softint clear, WO */ +#endif +                default: +                    goto illegal_insn; +                } +#if !defined(CONFIG_USER_ONLY) +            } else if (xop == 0x29) { /* rdpsr / UA2005 rdhpr */ +#ifndef TARGET_SPARC64 +                if (!supervisor(dc)) { +                    goto priv_insn; +                } +                update_psr(dc); +                gen_helper_rdpsr(cpu_dst, cpu_env); +#else +                CHECK_IU_FEATURE(dc, HYPV); +                if (!hypervisor(dc)) +                    goto priv_insn; +                rs1 = GET_FIELD(insn, 13, 17); +                switch (rs1) { +                case 0: // hpstate +                    // gen_op_rdhpstate(); +                    break; +                case 1: // htstate +                    // gen_op_rdhtstate(); +                    break; +                case 3: // hintp +                    tcg_gen_mov_tl(cpu_dst, cpu_hintp); +                    break; +                case 5: // htba +                    tcg_gen_mov_tl(cpu_dst, cpu_htba); +                    break; +                case 6: // hver +                    tcg_gen_mov_tl(cpu_dst, cpu_hver); +                    break; +                case 31: // hstick_cmpr +                    tcg_gen_mov_tl(cpu_dst, cpu_hstick_cmpr); +                    break; +                default: +                    goto illegal_insn; +                } +#endif +                gen_store_gpr(dc, rd, cpu_dst); +                break; +            } else if (xop == 0x2a) { /* rdwim / V9 rdpr */ +                if (!supervisor(dc)) { +                    goto priv_insn; +                } +                cpu_tmp0 = get_temp_tl(dc); +#ifdef TARGET_SPARC64 +                rs1 = GET_FIELD(insn, 13, 17); +                switch (rs1) { +                case 0: // tpc +                    { +                        TCGv_ptr r_tsptr; + +                        r_tsptr = tcg_temp_new_ptr(); +                        gen_load_trap_state_at_tl(r_tsptr, cpu_env); +                        tcg_gen_ld_tl(cpu_tmp0, r_tsptr, +                                      offsetof(trap_state, tpc)); +                        tcg_temp_free_ptr(r_tsptr); +                    } +                    break; +                case 1: // tnpc +                    { +                        TCGv_ptr r_tsptr; + +                        r_tsptr = tcg_temp_new_ptr(); +                        gen_load_trap_state_at_tl(r_tsptr, cpu_env); +                        tcg_gen_ld_tl(cpu_tmp0, r_tsptr, +                                      offsetof(trap_state, tnpc)); +                        tcg_temp_free_ptr(r_tsptr); +                    } +                    break; +                case 2: // tstate +                    { +                        TCGv_ptr r_tsptr; + +                        r_tsptr = tcg_temp_new_ptr(); +                        gen_load_trap_state_at_tl(r_tsptr, cpu_env); +                        tcg_gen_ld_tl(cpu_tmp0, r_tsptr, +                                      offsetof(trap_state, tstate)); +                        tcg_temp_free_ptr(r_tsptr); +                    } +                    break; +                case 3: // tt +                    { +                        TCGv_ptr r_tsptr = tcg_temp_new_ptr(); + +                        gen_load_trap_state_at_tl(r_tsptr, cpu_env); +                        tcg_gen_ld32s_tl(cpu_tmp0, r_tsptr, +                                         offsetof(trap_state, tt)); +                        tcg_temp_free_ptr(r_tsptr); +                    } +                    break; +                case 4: // tick +                    { +                        TCGv_ptr r_tickptr; + +                        r_tickptr = tcg_temp_new_ptr(); +                        tcg_gen_ld_ptr(r_tickptr, cpu_env, +                                       offsetof(CPUSPARCState, tick)); +                        gen_helper_tick_get_count(cpu_tmp0, r_tickptr); +                        tcg_temp_free_ptr(r_tickptr); +                    } +                    break; +                case 5: // tba +                    tcg_gen_mov_tl(cpu_tmp0, cpu_tbr); +                    break; +                case 6: // pstate +                    tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, +                                     offsetof(CPUSPARCState, pstate)); +                    break; +                case 7: // tl +                    tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, +                                     offsetof(CPUSPARCState, tl)); +                    break; +                case 8: // pil +                    tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, +                                     offsetof(CPUSPARCState, psrpil)); +                    break; +                case 9: // cwp +                    gen_helper_rdcwp(cpu_tmp0, cpu_env); +                    break; +                case 10: // cansave +                    tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, +                                     offsetof(CPUSPARCState, cansave)); +                    break; +                case 11: // canrestore +                    tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, +                                     offsetof(CPUSPARCState, canrestore)); +                    break; +                case 12: // cleanwin +                    tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, +                                     offsetof(CPUSPARCState, cleanwin)); +                    break; +                case 13: // otherwin +                    tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, +                                     offsetof(CPUSPARCState, otherwin)); +                    break; +                case 14: // wstate +                    tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, +                                     offsetof(CPUSPARCState, wstate)); +                    break; +                case 16: // UA2005 gl +                    CHECK_IU_FEATURE(dc, GL); +                    tcg_gen_ld32s_tl(cpu_tmp0, cpu_env, +                                     offsetof(CPUSPARCState, gl)); +                    break; +                case 26: // UA2005 strand status +                    CHECK_IU_FEATURE(dc, HYPV); +                    if (!hypervisor(dc)) +                        goto priv_insn; +                    tcg_gen_mov_tl(cpu_tmp0, cpu_ssr); +                    break; +                case 31: // ver +                    tcg_gen_mov_tl(cpu_tmp0, cpu_ver); +                    break; +                case 15: // fq +                default: +                    goto illegal_insn; +                } +#else +                tcg_gen_ext_i32_tl(cpu_tmp0, cpu_wim); +#endif +                gen_store_gpr(dc, rd, cpu_tmp0); +                break; +            } else if (xop == 0x2b) { /* rdtbr / V9 flushw */ +#ifdef TARGET_SPARC64 +                save_state(dc); +                gen_helper_flushw(cpu_env); +#else +                if (!supervisor(dc)) +                    goto priv_insn; +                gen_store_gpr(dc, rd, cpu_tbr); +#endif +                break; +#endif +            } else if (xop == 0x34) {   /* FPU Operations */ +                if (gen_trap_ifnofpu(dc)) { +                    goto jmp_insn; +                } +                gen_op_clear_ieee_excp_and_FTT(); +                rs1 = GET_FIELD(insn, 13, 17); +                rs2 = GET_FIELD(insn, 27, 31); +                xop = GET_FIELD(insn, 18, 26); +                save_state(dc); +                switch (xop) { +                case 0x1: /* fmovs */ +                    cpu_src1_32 = gen_load_fpr_F(dc, rs2); +                    gen_store_fpr_F(dc, rd, cpu_src1_32); +                    break; +                case 0x5: /* fnegs */ +                    gen_ne_fop_FF(dc, rd, rs2, gen_helper_fnegs); +                    break; +                case 0x9: /* fabss */ +                    gen_ne_fop_FF(dc, rd, rs2, gen_helper_fabss); +                    break; +                case 0x29: /* fsqrts */ +                    CHECK_FPU_FEATURE(dc, FSQRT); +                    gen_fop_FF(dc, rd, rs2, gen_helper_fsqrts); +                    break; +                case 0x2a: /* fsqrtd */ +                    CHECK_FPU_FEATURE(dc, FSQRT); +                    gen_fop_DD(dc, rd, rs2, gen_helper_fsqrtd); +                    break; +                case 0x2b: /* fsqrtq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_fop_QQ(dc, rd, rs2, gen_helper_fsqrtq); +                    break; +                case 0x41: /* fadds */ +                    gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fadds); +                    break; +                case 0x42: /* faddd */ +                    gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_faddd); +                    break; +                case 0x43: /* faddq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_faddq); +                    break; +                case 0x45: /* fsubs */ +                    gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fsubs); +                    break; +                case 0x46: /* fsubd */ +                    gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fsubd); +                    break; +                case 0x47: /* fsubq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fsubq); +                    break; +                case 0x49: /* fmuls */ +                    CHECK_FPU_FEATURE(dc, FMUL); +                    gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fmuls); +                    break; +                case 0x4a: /* fmuld */ +                    CHECK_FPU_FEATURE(dc, FMUL); +                    gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld); +                    break; +                case 0x4b: /* fmulq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    CHECK_FPU_FEATURE(dc, FMUL); +                    gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fmulq); +                    break; +                case 0x4d: /* fdivs */ +                    gen_fop_FFF(dc, rd, rs1, rs2, gen_helper_fdivs); +                    break; +                case 0x4e: /* fdivd */ +                    gen_fop_DDD(dc, rd, rs1, rs2, gen_helper_fdivd); +                    break; +                case 0x4f: /* fdivq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_fop_QQQ(dc, rd, rs1, rs2, gen_helper_fdivq); +                    break; +                case 0x69: /* fsmuld */ +                    CHECK_FPU_FEATURE(dc, FSMULD); +                    gen_fop_DFF(dc, rd, rs1, rs2, gen_helper_fsmuld); +                    break; +                case 0x6e: /* fdmulq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_fop_QDD(dc, rd, rs1, rs2, gen_helper_fdmulq); +                    break; +                case 0xc4: /* fitos */ +                    gen_fop_FF(dc, rd, rs2, gen_helper_fitos); +                    break; +                case 0xc6: /* fdtos */ +                    gen_fop_FD(dc, rd, rs2, gen_helper_fdtos); +                    break; +                case 0xc7: /* fqtos */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_fop_FQ(dc, rd, rs2, gen_helper_fqtos); +                    break; +                case 0xc8: /* fitod */ +                    gen_ne_fop_DF(dc, rd, rs2, gen_helper_fitod); +                    break; +                case 0xc9: /* fstod */ +                    gen_ne_fop_DF(dc, rd, rs2, gen_helper_fstod); +                    break; +                case 0xcb: /* fqtod */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_fop_DQ(dc, rd, rs2, gen_helper_fqtod); +                    break; +                case 0xcc: /* fitoq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_ne_fop_QF(dc, rd, rs2, gen_helper_fitoq); +                    break; +                case 0xcd: /* fstoq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_ne_fop_QF(dc, rd, rs2, gen_helper_fstoq); +                    break; +                case 0xce: /* fdtoq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_ne_fop_QD(dc, rd, rs2, gen_helper_fdtoq); +                    break; +                case 0xd1: /* fstoi */ +                    gen_fop_FF(dc, rd, rs2, gen_helper_fstoi); +                    break; +                case 0xd2: /* fdtoi */ +                    gen_fop_FD(dc, rd, rs2, gen_helper_fdtoi); +                    break; +                case 0xd3: /* fqtoi */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_fop_FQ(dc, rd, rs2, gen_helper_fqtoi); +                    break; +#ifdef TARGET_SPARC64 +                case 0x2: /* V9 fmovd */ +                    cpu_src1_64 = gen_load_fpr_D(dc, rs2); +                    gen_store_fpr_D(dc, rd, cpu_src1_64); +                    break; +                case 0x3: /* V9 fmovq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_move_Q(rd, rs2); +                    break; +                case 0x6: /* V9 fnegd */ +                    gen_ne_fop_DD(dc, rd, rs2, gen_helper_fnegd); +                    break; +                case 0x7: /* V9 fnegq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fnegq); +                    break; +                case 0xa: /* V9 fabsd */ +                    gen_ne_fop_DD(dc, rd, rs2, gen_helper_fabsd); +                    break; +                case 0xb: /* V9 fabsq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_ne_fop_QQ(dc, rd, rs2, gen_helper_fabsq); +                    break; +                case 0x81: /* V9 fstox */ +                    gen_fop_DF(dc, rd, rs2, gen_helper_fstox); +                    break; +                case 0x82: /* V9 fdtox */ +                    gen_fop_DD(dc, rd, rs2, gen_helper_fdtox); +                    break; +                case 0x83: /* V9 fqtox */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_fop_DQ(dc, rd, rs2, gen_helper_fqtox); +                    break; +                case 0x84: /* V9 fxtos */ +                    gen_fop_FD(dc, rd, rs2, gen_helper_fxtos); +                    break; +                case 0x88: /* V9 fxtod */ +                    gen_fop_DD(dc, rd, rs2, gen_helper_fxtod); +                    break; +                case 0x8c: /* V9 fxtoq */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    gen_ne_fop_QD(dc, rd, rs2, gen_helper_fxtoq); +                    break; +#endif +                default: +                    goto illegal_insn; +                } +            } else if (xop == 0x35) {   /* FPU Operations */ +#ifdef TARGET_SPARC64 +                int cond; +#endif +                if (gen_trap_ifnofpu(dc)) { +                    goto jmp_insn; +                } +                gen_op_clear_ieee_excp_and_FTT(); +                rs1 = GET_FIELD(insn, 13, 17); +                rs2 = GET_FIELD(insn, 27, 31); +                xop = GET_FIELD(insn, 18, 26); +                save_state(dc); + +#ifdef TARGET_SPARC64 +#define FMOVR(sz)                                                  \ +                do {                                               \ +                    DisasCompare cmp;                              \ +                    cond = GET_FIELD_SP(insn, 10, 12);             \ +                    cpu_src1 = get_src1(dc, insn);                 \ +                    gen_compare_reg(&cmp, cond, cpu_src1);         \ +                    gen_fmov##sz(dc, &cmp, rd, rs2);               \ +                    free_compare(&cmp);                            \ +                } while (0) + +                if ((xop & 0x11f) == 0x005) { /* V9 fmovsr */ +                    FMOVR(s); +                    break; +                } else if ((xop & 0x11f) == 0x006) { // V9 fmovdr +                    FMOVR(d); +                    break; +                } else if ((xop & 0x11f) == 0x007) { // V9 fmovqr +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    FMOVR(q); +                    break; +                } +#undef FMOVR +#endif +                switch (xop) { +#ifdef TARGET_SPARC64 +#define FMOVCC(fcc, sz)                                                 \ +                    do {                                                \ +                        DisasCompare cmp;                               \ +                        cond = GET_FIELD_SP(insn, 14, 17);              \ +                        gen_fcompare(&cmp, fcc, cond);                  \ +                        gen_fmov##sz(dc, &cmp, rd, rs2);                \ +                        free_compare(&cmp);                             \ +                    } while (0) + +                    case 0x001: /* V9 fmovscc %fcc0 */ +                        FMOVCC(0, s); +                        break; +                    case 0x002: /* V9 fmovdcc %fcc0 */ +                        FMOVCC(0, d); +                        break; +                    case 0x003: /* V9 fmovqcc %fcc0 */ +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        FMOVCC(0, q); +                        break; +                    case 0x041: /* V9 fmovscc %fcc1 */ +                        FMOVCC(1, s); +                        break; +                    case 0x042: /* V9 fmovdcc %fcc1 */ +                        FMOVCC(1, d); +                        break; +                    case 0x043: /* V9 fmovqcc %fcc1 */ +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        FMOVCC(1, q); +                        break; +                    case 0x081: /* V9 fmovscc %fcc2 */ +                        FMOVCC(2, s); +                        break; +                    case 0x082: /* V9 fmovdcc %fcc2 */ +                        FMOVCC(2, d); +                        break; +                    case 0x083: /* V9 fmovqcc %fcc2 */ +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        FMOVCC(2, q); +                        break; +                    case 0x0c1: /* V9 fmovscc %fcc3 */ +                        FMOVCC(3, s); +                        break; +                    case 0x0c2: /* V9 fmovdcc %fcc3 */ +                        FMOVCC(3, d); +                        break; +                    case 0x0c3: /* V9 fmovqcc %fcc3 */ +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        FMOVCC(3, q); +                        break; +#undef FMOVCC +#define FMOVCC(xcc, sz)                                                 \ +                    do {                                                \ +                        DisasCompare cmp;                               \ +                        cond = GET_FIELD_SP(insn, 14, 17);              \ +                        gen_compare(&cmp, xcc, cond, dc);               \ +                        gen_fmov##sz(dc, &cmp, rd, rs2);                \ +                        free_compare(&cmp);                             \ +                    } while (0) + +                    case 0x101: /* V9 fmovscc %icc */ +                        FMOVCC(0, s); +                        break; +                    case 0x102: /* V9 fmovdcc %icc */ +                        FMOVCC(0, d); +                        break; +                    case 0x103: /* V9 fmovqcc %icc */ +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        FMOVCC(0, q); +                        break; +                    case 0x181: /* V9 fmovscc %xcc */ +                        FMOVCC(1, s); +                        break; +                    case 0x182: /* V9 fmovdcc %xcc */ +                        FMOVCC(1, d); +                        break; +                    case 0x183: /* V9 fmovqcc %xcc */ +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        FMOVCC(1, q); +                        break; +#undef FMOVCC +#endif +                    case 0x51: /* fcmps, V9 %fcc */ +                        cpu_src1_32 = gen_load_fpr_F(dc, rs1); +                        cpu_src2_32 = gen_load_fpr_F(dc, rs2); +                        gen_op_fcmps(rd & 3, cpu_src1_32, cpu_src2_32); +                        break; +                    case 0x52: /* fcmpd, V9 %fcc */ +                        cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                        cpu_src2_64 = gen_load_fpr_D(dc, rs2); +                        gen_op_fcmpd(rd & 3, cpu_src1_64, cpu_src2_64); +                        break; +                    case 0x53: /* fcmpq, V9 %fcc */ +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        gen_op_load_fpr_QT0(QFPREG(rs1)); +                        gen_op_load_fpr_QT1(QFPREG(rs2)); +                        gen_op_fcmpq(rd & 3); +                        break; +                    case 0x55: /* fcmpes, V9 %fcc */ +                        cpu_src1_32 = gen_load_fpr_F(dc, rs1); +                        cpu_src2_32 = gen_load_fpr_F(dc, rs2); +                        gen_op_fcmpes(rd & 3, cpu_src1_32, cpu_src2_32); +                        break; +                    case 0x56: /* fcmped, V9 %fcc */ +                        cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                        cpu_src2_64 = gen_load_fpr_D(dc, rs2); +                        gen_op_fcmped(rd & 3, cpu_src1_64, cpu_src2_64); +                        break; +                    case 0x57: /* fcmpeq, V9 %fcc */ +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        gen_op_load_fpr_QT0(QFPREG(rs1)); +                        gen_op_load_fpr_QT1(QFPREG(rs2)); +                        gen_op_fcmpeq(rd & 3); +                        break; +                    default: +                        goto illegal_insn; +                } +            } else if (xop == 0x2) { +                TCGv dst = gen_dest_gpr(dc, rd); +                rs1 = GET_FIELD(insn, 13, 17); +                if (rs1 == 0) { +                    /* clr/mov shortcut : or %g0, x, y -> mov x, y */ +                    if (IS_IMM) {       /* immediate */ +                        simm = GET_FIELDs(insn, 19, 31); +                        tcg_gen_movi_tl(dst, simm); +                        gen_store_gpr(dc, rd, dst); +                    } else {            /* register */ +                        rs2 = GET_FIELD(insn, 27, 31); +                        if (rs2 == 0) { +                            tcg_gen_movi_tl(dst, 0); +                            gen_store_gpr(dc, rd, dst); +                        } else { +                            cpu_src2 = gen_load_gpr(dc, rs2); +                            gen_store_gpr(dc, rd, cpu_src2); +                        } +                    } +                } else { +                    cpu_src1 = get_src1(dc, insn); +                    if (IS_IMM) {       /* immediate */ +                        simm = GET_FIELDs(insn, 19, 31); +                        tcg_gen_ori_tl(dst, cpu_src1, simm); +                        gen_store_gpr(dc, rd, dst); +                    } else {            /* register */ +                        rs2 = GET_FIELD(insn, 27, 31); +                        if (rs2 == 0) { +                            /* mov shortcut:  or x, %g0, y -> mov x, y */ +                            gen_store_gpr(dc, rd, cpu_src1); +                        } else { +                            cpu_src2 = gen_load_gpr(dc, rs2); +                            tcg_gen_or_tl(dst, cpu_src1, cpu_src2); +                            gen_store_gpr(dc, rd, dst); +                        } +                    } +                } +#ifdef TARGET_SPARC64 +            } else if (xop == 0x25) { /* sll, V9 sllx */ +                cpu_src1 = get_src1(dc, insn); +                if (IS_IMM) {   /* immediate */ +                    simm = GET_FIELDs(insn, 20, 31); +                    if (insn & (1 << 12)) { +                        tcg_gen_shli_i64(cpu_dst, cpu_src1, simm & 0x3f); +                    } else { +                        tcg_gen_shli_i64(cpu_dst, cpu_src1, simm & 0x1f); +                    } +                } else {                /* register */ +                    rs2 = GET_FIELD(insn, 27, 31); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    cpu_tmp0 = get_temp_tl(dc); +                    if (insn & (1 << 12)) { +                        tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f); +                    } else { +                        tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f); +                    } +                    tcg_gen_shl_i64(cpu_dst, cpu_src1, cpu_tmp0); +                } +                gen_store_gpr(dc, rd, cpu_dst); +            } else if (xop == 0x26) { /* srl, V9 srlx */ +                cpu_src1 = get_src1(dc, insn); +                if (IS_IMM) {   /* immediate */ +                    simm = GET_FIELDs(insn, 20, 31); +                    if (insn & (1 << 12)) { +                        tcg_gen_shri_i64(cpu_dst, cpu_src1, simm & 0x3f); +                    } else { +                        tcg_gen_andi_i64(cpu_dst, cpu_src1, 0xffffffffULL); +                        tcg_gen_shri_i64(cpu_dst, cpu_dst, simm & 0x1f); +                    } +                } else {                /* register */ +                    rs2 = GET_FIELD(insn, 27, 31); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    cpu_tmp0 = get_temp_tl(dc); +                    if (insn & (1 << 12)) { +                        tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f); +                        tcg_gen_shr_i64(cpu_dst, cpu_src1, cpu_tmp0); +                    } else { +                        tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f); +                        tcg_gen_andi_i64(cpu_dst, cpu_src1, 0xffffffffULL); +                        tcg_gen_shr_i64(cpu_dst, cpu_dst, cpu_tmp0); +                    } +                } +                gen_store_gpr(dc, rd, cpu_dst); +            } else if (xop == 0x27) { /* sra, V9 srax */ +                cpu_src1 = get_src1(dc, insn); +                if (IS_IMM) {   /* immediate */ +                    simm = GET_FIELDs(insn, 20, 31); +                    if (insn & (1 << 12)) { +                        tcg_gen_sari_i64(cpu_dst, cpu_src1, simm & 0x3f); +                    } else { +                        tcg_gen_ext32s_i64(cpu_dst, cpu_src1); +                        tcg_gen_sari_i64(cpu_dst, cpu_dst, simm & 0x1f); +                    } +                } else {                /* register */ +                    rs2 = GET_FIELD(insn, 27, 31); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    cpu_tmp0 = get_temp_tl(dc); +                    if (insn & (1 << 12)) { +                        tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x3f); +                        tcg_gen_sar_i64(cpu_dst, cpu_src1, cpu_tmp0); +                    } else { +                        tcg_gen_andi_i64(cpu_tmp0, cpu_src2, 0x1f); +                        tcg_gen_ext32s_i64(cpu_dst, cpu_src1); +                        tcg_gen_sar_i64(cpu_dst, cpu_dst, cpu_tmp0); +                    } +                } +                gen_store_gpr(dc, rd, cpu_dst); +#endif +            } else if (xop < 0x36) { +                if (xop < 0x20) { +                    cpu_src1 = get_src1(dc, insn); +                    cpu_src2 = get_src2(dc, insn); +                    switch (xop & ~0x10) { +                    case 0x0: /* add */ +                        if (xop & 0x10) { +                            gen_op_add_cc(cpu_dst, cpu_src1, cpu_src2); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADD); +                            dc->cc_op = CC_OP_ADD; +                        } else { +                            tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2); +                        } +                        break; +                    case 0x1: /* and */ +                        tcg_gen_and_tl(cpu_dst, cpu_src1, cpu_src2); +                        if (xop & 0x10) { +                            tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); +                            dc->cc_op = CC_OP_LOGIC; +                        } +                        break; +                    case 0x2: /* or */ +                        tcg_gen_or_tl(cpu_dst, cpu_src1, cpu_src2); +                        if (xop & 0x10) { +                            tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); +                            dc->cc_op = CC_OP_LOGIC; +                        } +                        break; +                    case 0x3: /* xor */ +                        tcg_gen_xor_tl(cpu_dst, cpu_src1, cpu_src2); +                        if (xop & 0x10) { +                            tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); +                            dc->cc_op = CC_OP_LOGIC; +                        } +                        break; +                    case 0x4: /* sub */ +                        if (xop & 0x10) { +                            gen_op_sub_cc(cpu_dst, cpu_src1, cpu_src2); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_SUB); +                            dc->cc_op = CC_OP_SUB; +                        } else { +                            tcg_gen_sub_tl(cpu_dst, cpu_src1, cpu_src2); +                        } +                        break; +                    case 0x5: /* andn */ +                        tcg_gen_andc_tl(cpu_dst, cpu_src1, cpu_src2); +                        if (xop & 0x10) { +                            tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); +                            dc->cc_op = CC_OP_LOGIC; +                        } +                        break; +                    case 0x6: /* orn */ +                        tcg_gen_orc_tl(cpu_dst, cpu_src1, cpu_src2); +                        if (xop & 0x10) { +                            tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); +                            dc->cc_op = CC_OP_LOGIC; +                        } +                        break; +                    case 0x7: /* xorn */ +                        tcg_gen_eqv_tl(cpu_dst, cpu_src1, cpu_src2); +                        if (xop & 0x10) { +                            tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); +                            dc->cc_op = CC_OP_LOGIC; +                        } +                        break; +                    case 0x8: /* addx, V9 addc */ +                        gen_op_addx_int(dc, cpu_dst, cpu_src1, cpu_src2, +                                        (xop & 0x10)); +                        break; +#ifdef TARGET_SPARC64 +                    case 0x9: /* V9 mulx */ +                        tcg_gen_mul_i64(cpu_dst, cpu_src1, cpu_src2); +                        break; +#endif +                    case 0xa: /* umul */ +                        CHECK_IU_FEATURE(dc, MUL); +                        gen_op_umul(cpu_dst, cpu_src1, cpu_src2); +                        if (xop & 0x10) { +                            tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); +                            dc->cc_op = CC_OP_LOGIC; +                        } +                        break; +                    case 0xb: /* smul */ +                        CHECK_IU_FEATURE(dc, MUL); +                        gen_op_smul(cpu_dst, cpu_src1, cpu_src2); +                        if (xop & 0x10) { +                            tcg_gen_mov_tl(cpu_cc_dst, cpu_dst); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_LOGIC); +                            dc->cc_op = CC_OP_LOGIC; +                        } +                        break; +                    case 0xc: /* subx, V9 subc */ +                        gen_op_subx_int(dc, cpu_dst, cpu_src1, cpu_src2, +                                        (xop & 0x10)); +                        break; +#ifdef TARGET_SPARC64 +                    case 0xd: /* V9 udivx */ +                        gen_helper_udivx(cpu_dst, cpu_env, cpu_src1, cpu_src2); +                        break; +#endif +                    case 0xe: /* udiv */ +                        CHECK_IU_FEATURE(dc, DIV); +                        if (xop & 0x10) { +                            gen_helper_udiv_cc(cpu_dst, cpu_env, cpu_src1, +                                               cpu_src2); +                            dc->cc_op = CC_OP_DIV; +                        } else { +                            gen_helper_udiv(cpu_dst, cpu_env, cpu_src1, +                                            cpu_src2); +                        } +                        break; +                    case 0xf: /* sdiv */ +                        CHECK_IU_FEATURE(dc, DIV); +                        if (xop & 0x10) { +                            gen_helper_sdiv_cc(cpu_dst, cpu_env, cpu_src1, +                                               cpu_src2); +                            dc->cc_op = CC_OP_DIV; +                        } else { +                            gen_helper_sdiv(cpu_dst, cpu_env, cpu_src1, +                                            cpu_src2); +                        } +                        break; +                    default: +                        goto illegal_insn; +                    } +                    gen_store_gpr(dc, rd, cpu_dst); +                } else { +                    cpu_src1 = get_src1(dc, insn); +                    cpu_src2 = get_src2(dc, insn); +                    switch (xop) { +                    case 0x20: /* taddcc */ +                        gen_op_add_cc(cpu_dst, cpu_src1, cpu_src2); +                        gen_store_gpr(dc, rd, cpu_dst); +                        tcg_gen_movi_i32(cpu_cc_op, CC_OP_TADD); +                        dc->cc_op = CC_OP_TADD; +                        break; +                    case 0x21: /* tsubcc */ +                        gen_op_sub_cc(cpu_dst, cpu_src1, cpu_src2); +                        gen_store_gpr(dc, rd, cpu_dst); +                        tcg_gen_movi_i32(cpu_cc_op, CC_OP_TSUB); +                        dc->cc_op = CC_OP_TSUB; +                        break; +                    case 0x22: /* taddcctv */ +                        gen_helper_taddcctv(cpu_dst, cpu_env, +                                            cpu_src1, cpu_src2); +                        gen_store_gpr(dc, rd, cpu_dst); +                        dc->cc_op = CC_OP_TADDTV; +                        break; +                    case 0x23: /* tsubcctv */ +                        gen_helper_tsubcctv(cpu_dst, cpu_env, +                                            cpu_src1, cpu_src2); +                        gen_store_gpr(dc, rd, cpu_dst); +                        dc->cc_op = CC_OP_TSUBTV; +                        break; +                    case 0x24: /* mulscc */ +                        update_psr(dc); +                        gen_op_mulscc(cpu_dst, cpu_src1, cpu_src2); +                        gen_store_gpr(dc, rd, cpu_dst); +                        tcg_gen_movi_i32(cpu_cc_op, CC_OP_ADD); +                        dc->cc_op = CC_OP_ADD; +                        break; +#ifndef TARGET_SPARC64 +                    case 0x25:  /* sll */ +                        if (IS_IMM) { /* immediate */ +                            simm = GET_FIELDs(insn, 20, 31); +                            tcg_gen_shli_tl(cpu_dst, cpu_src1, simm & 0x1f); +                        } else { /* register */ +                            cpu_tmp0 = get_temp_tl(dc); +                            tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f); +                            tcg_gen_shl_tl(cpu_dst, cpu_src1, cpu_tmp0); +                        } +                        gen_store_gpr(dc, rd, cpu_dst); +                        break; +                    case 0x26:  /* srl */ +                        if (IS_IMM) { /* immediate */ +                            simm = GET_FIELDs(insn, 20, 31); +                            tcg_gen_shri_tl(cpu_dst, cpu_src1, simm & 0x1f); +                        } else { /* register */ +                            cpu_tmp0 = get_temp_tl(dc); +                            tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f); +                            tcg_gen_shr_tl(cpu_dst, cpu_src1, cpu_tmp0); +                        } +                        gen_store_gpr(dc, rd, cpu_dst); +                        break; +                    case 0x27:  /* sra */ +                        if (IS_IMM) { /* immediate */ +                            simm = GET_FIELDs(insn, 20, 31); +                            tcg_gen_sari_tl(cpu_dst, cpu_src1, simm & 0x1f); +                        } else { /* register */ +                            cpu_tmp0 = get_temp_tl(dc); +                            tcg_gen_andi_tl(cpu_tmp0, cpu_src2, 0x1f); +                            tcg_gen_sar_tl(cpu_dst, cpu_src1, cpu_tmp0); +                        } +                        gen_store_gpr(dc, rd, cpu_dst); +                        break; +#endif +                    case 0x30: +                        { +                            cpu_tmp0 = get_temp_tl(dc); +                            switch(rd) { +                            case 0: /* wry */ +                                tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +                                tcg_gen_andi_tl(cpu_y, cpu_tmp0, 0xffffffff); +                                break; +#ifndef TARGET_SPARC64 +                            case 0x01 ... 0x0f: /* undefined in the +                                                   SPARCv8 manual, nop +                                                   on the microSPARC +                                                   II */ +                            case 0x10 ... 0x1f: /* implementation-dependent +                                                   in the SPARCv8 +                                                   manual, nop on the +                                                   microSPARC II */ +                                if ((rd == 0x13) && (dc->def->features & +                                                     CPU_FEATURE_POWERDOWN)) { +                                    /* LEON3 power-down */ +                                    save_state(dc); +                                    gen_helper_power_down(cpu_env); +                                } +                                break; +#else +                            case 0x2: /* V9 wrccr */ +                                tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +                                gen_helper_wrccr(cpu_env, cpu_tmp0); +                                tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS); +                                dc->cc_op = CC_OP_FLAGS; +                                break; +                            case 0x3: /* V9 wrasi */ +                                tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +                                tcg_gen_andi_tl(cpu_tmp0, cpu_tmp0, 0xff); +                                tcg_gen_trunc_tl_i32(cpu_asi, cpu_tmp0); +                                break; +                            case 0x6: /* V9 wrfprs */ +                                tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +                                tcg_gen_trunc_tl_i32(cpu_fprs, cpu_tmp0); +                                save_state(dc); +                                gen_op_next_insn(); +                                tcg_gen_exit_tb(0); +                                dc->is_br = 1; +                                break; +                            case 0xf: /* V9 sir, nop if user */ +#if !defined(CONFIG_USER_ONLY) +                                if (supervisor(dc)) { +                                    ; // XXX +                                } +#endif +                                break; +                            case 0x13: /* Graphics Status */ +                                if (gen_trap_ifnofpu(dc)) { +                                    goto jmp_insn; +                                } +                                tcg_gen_xor_tl(cpu_gsr, cpu_src1, cpu_src2); +                                break; +                            case 0x14: /* Softint set */ +                                if (!supervisor(dc)) +                                    goto illegal_insn; +                                tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +                                gen_helper_set_softint(cpu_env, cpu_tmp0); +                                break; +                            case 0x15: /* Softint clear */ +                                if (!supervisor(dc)) +                                    goto illegal_insn; +                                tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +                                gen_helper_clear_softint(cpu_env, cpu_tmp0); +                                break; +                            case 0x16: /* Softint write */ +                                if (!supervisor(dc)) +                                    goto illegal_insn; +                                tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +                                gen_helper_write_softint(cpu_env, cpu_tmp0); +                                break; +                            case 0x17: /* Tick compare */ +#if !defined(CONFIG_USER_ONLY) +                                if (!supervisor(dc)) +                                    goto illegal_insn; +#endif +                                { +                                    TCGv_ptr r_tickptr; + +                                    tcg_gen_xor_tl(cpu_tick_cmpr, cpu_src1, +                                                   cpu_src2); +                                    r_tickptr = tcg_temp_new_ptr(); +                                    tcg_gen_ld_ptr(r_tickptr, cpu_env, +                                                   offsetof(CPUSPARCState, tick)); +                                    gen_helper_tick_set_limit(r_tickptr, +                                                              cpu_tick_cmpr); +                                    tcg_temp_free_ptr(r_tickptr); +                                } +                                break; +                            case 0x18: /* System tick */ +#if !defined(CONFIG_USER_ONLY) +                                if (!supervisor(dc)) +                                    goto illegal_insn; +#endif +                                { +                                    TCGv_ptr r_tickptr; + +                                    tcg_gen_xor_tl(cpu_tmp0, cpu_src1, +                                                   cpu_src2); +                                    r_tickptr = tcg_temp_new_ptr(); +                                    tcg_gen_ld_ptr(r_tickptr, cpu_env, +                                                   offsetof(CPUSPARCState, stick)); +                                    gen_helper_tick_set_count(r_tickptr, +                                                              cpu_tmp0); +                                    tcg_temp_free_ptr(r_tickptr); +                                } +                                break; +                            case 0x19: /* System tick compare */ +#if !defined(CONFIG_USER_ONLY) +                                if (!supervisor(dc)) +                                    goto illegal_insn; +#endif +                                { +                                    TCGv_ptr r_tickptr; + +                                    tcg_gen_xor_tl(cpu_stick_cmpr, cpu_src1, +                                                   cpu_src2); +                                    r_tickptr = tcg_temp_new_ptr(); +                                    tcg_gen_ld_ptr(r_tickptr, cpu_env, +                                                   offsetof(CPUSPARCState, stick)); +                                    gen_helper_tick_set_limit(r_tickptr, +                                                              cpu_stick_cmpr); +                                    tcg_temp_free_ptr(r_tickptr); +                                } +                                break; + +                            case 0x10: /* Performance Control */ +                            case 0x11: /* Performance Instrumentation +                                          Counter */ +                            case 0x12: /* Dispatch Control */ +#endif +                            default: +                                goto illegal_insn; +                            } +                        } +                        break; +#if !defined(CONFIG_USER_ONLY) +                    case 0x31: /* wrpsr, V9 saved, restored */ +                        { +                            if (!supervisor(dc)) +                                goto priv_insn; +#ifdef TARGET_SPARC64 +                            switch (rd) { +                            case 0: +                                gen_helper_saved(cpu_env); +                                break; +                            case 1: +                                gen_helper_restored(cpu_env); +                                break; +                            case 2: /* UA2005 allclean */ +                            case 3: /* UA2005 otherw */ +                            case 4: /* UA2005 normalw */ +                            case 5: /* UA2005 invalw */ +                                // XXX +                            default: +                                goto illegal_insn; +                            } +#else +                            cpu_tmp0 = get_temp_tl(dc); +                            tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +                            gen_helper_wrpsr(cpu_env, cpu_tmp0); +                            tcg_gen_movi_i32(cpu_cc_op, CC_OP_FLAGS); +                            dc->cc_op = CC_OP_FLAGS; +                            save_state(dc); +                            gen_op_next_insn(); +                            tcg_gen_exit_tb(0); +                            dc->is_br = 1; +#endif +                        } +                        break; +                    case 0x32: /* wrwim, V9 wrpr */ +                        { +                            if (!supervisor(dc)) +                                goto priv_insn; +                            cpu_tmp0 = get_temp_tl(dc); +                            tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +#ifdef TARGET_SPARC64 +                            switch (rd) { +                            case 0: // tpc +                                { +                                    TCGv_ptr r_tsptr; + +                                    r_tsptr = tcg_temp_new_ptr(); +                                    gen_load_trap_state_at_tl(r_tsptr, cpu_env); +                                    tcg_gen_st_tl(cpu_tmp0, r_tsptr, +                                                  offsetof(trap_state, tpc)); +                                    tcg_temp_free_ptr(r_tsptr); +                                } +                                break; +                            case 1: // tnpc +                                { +                                    TCGv_ptr r_tsptr; + +                                    r_tsptr = tcg_temp_new_ptr(); +                                    gen_load_trap_state_at_tl(r_tsptr, cpu_env); +                                    tcg_gen_st_tl(cpu_tmp0, r_tsptr, +                                                  offsetof(trap_state, tnpc)); +                                    tcg_temp_free_ptr(r_tsptr); +                                } +                                break; +                            case 2: // tstate +                                { +                                    TCGv_ptr r_tsptr; + +                                    r_tsptr = tcg_temp_new_ptr(); +                                    gen_load_trap_state_at_tl(r_tsptr, cpu_env); +                                    tcg_gen_st_tl(cpu_tmp0, r_tsptr, +                                                  offsetof(trap_state, +                                                           tstate)); +                                    tcg_temp_free_ptr(r_tsptr); +                                } +                                break; +                            case 3: // tt +                                { +                                    TCGv_ptr r_tsptr; + +                                    r_tsptr = tcg_temp_new_ptr(); +                                    gen_load_trap_state_at_tl(r_tsptr, cpu_env); +                                    tcg_gen_st32_tl(cpu_tmp0, r_tsptr, +                                                    offsetof(trap_state, tt)); +                                    tcg_temp_free_ptr(r_tsptr); +                                } +                                break; +                            case 4: // tick +                                { +                                    TCGv_ptr r_tickptr; + +                                    r_tickptr = tcg_temp_new_ptr(); +                                    tcg_gen_ld_ptr(r_tickptr, cpu_env, +                                                   offsetof(CPUSPARCState, tick)); +                                    gen_helper_tick_set_count(r_tickptr, +                                                              cpu_tmp0); +                                    tcg_temp_free_ptr(r_tickptr); +                                } +                                break; +                            case 5: // tba +                                tcg_gen_mov_tl(cpu_tbr, cpu_tmp0); +                                break; +                            case 6: // pstate +                                save_state(dc); +                                gen_helper_wrpstate(cpu_env, cpu_tmp0); +                                dc->npc = DYNAMIC_PC; +                                break; +                            case 7: // tl +                                save_state(dc); +                                tcg_gen_st32_tl(cpu_tmp0, cpu_env, +                                               offsetof(CPUSPARCState, tl)); +                                dc->npc = DYNAMIC_PC; +                                break; +                            case 8: // pil +                                gen_helper_wrpil(cpu_env, cpu_tmp0); +                                break; +                            case 9: // cwp +                                gen_helper_wrcwp(cpu_env, cpu_tmp0); +                                break; +                            case 10: // cansave +                                tcg_gen_st32_tl(cpu_tmp0, cpu_env, +                                                offsetof(CPUSPARCState, +                                                         cansave)); +                                break; +                            case 11: // canrestore +                                tcg_gen_st32_tl(cpu_tmp0, cpu_env, +                                                offsetof(CPUSPARCState, +                                                         canrestore)); +                                break; +                            case 12: // cleanwin +                                tcg_gen_st32_tl(cpu_tmp0, cpu_env, +                                                offsetof(CPUSPARCState, +                                                         cleanwin)); +                                break; +                            case 13: // otherwin +                                tcg_gen_st32_tl(cpu_tmp0, cpu_env, +                                                offsetof(CPUSPARCState, +                                                         otherwin)); +                                break; +                            case 14: // wstate +                                tcg_gen_st32_tl(cpu_tmp0, cpu_env, +                                                offsetof(CPUSPARCState, +                                                         wstate)); +                                break; +                            case 16: // UA2005 gl +                                CHECK_IU_FEATURE(dc, GL); +                                tcg_gen_st32_tl(cpu_tmp0, cpu_env, +                                                offsetof(CPUSPARCState, gl)); +                                break; +                            case 26: // UA2005 strand status +                                CHECK_IU_FEATURE(dc, HYPV); +                                if (!hypervisor(dc)) +                                    goto priv_insn; +                                tcg_gen_mov_tl(cpu_ssr, cpu_tmp0); +                                break; +                            default: +                                goto illegal_insn; +                            } +#else +                            tcg_gen_trunc_tl_i32(cpu_wim, cpu_tmp0); +                            if (dc->def->nwindows != 32) { +                                tcg_gen_andi_tl(cpu_wim, cpu_wim, +                                                (1 << dc->def->nwindows) - 1); +                            } +#endif +                        } +                        break; +                    case 0x33: /* wrtbr, UA2005 wrhpr */ +                        { +#ifndef TARGET_SPARC64 +                            if (!supervisor(dc)) +                                goto priv_insn; +                            tcg_gen_xor_tl(cpu_tbr, cpu_src1, cpu_src2); +#else +                            CHECK_IU_FEATURE(dc, HYPV); +                            if (!hypervisor(dc)) +                                goto priv_insn; +                            cpu_tmp0 = get_temp_tl(dc); +                            tcg_gen_xor_tl(cpu_tmp0, cpu_src1, cpu_src2); +                            switch (rd) { +                            case 0: // hpstate +                                // XXX gen_op_wrhpstate(); +                                save_state(dc); +                                gen_op_next_insn(); +                                tcg_gen_exit_tb(0); +                                dc->is_br = 1; +                                break; +                            case 1: // htstate +                                // XXX gen_op_wrhtstate(); +                                break; +                            case 3: // hintp +                                tcg_gen_mov_tl(cpu_hintp, cpu_tmp0); +                                break; +                            case 5: // htba +                                tcg_gen_mov_tl(cpu_htba, cpu_tmp0); +                                break; +                            case 31: // hstick_cmpr +                                { +                                    TCGv_ptr r_tickptr; + +                                    tcg_gen_mov_tl(cpu_hstick_cmpr, cpu_tmp0); +                                    r_tickptr = tcg_temp_new_ptr(); +                                    tcg_gen_ld_ptr(r_tickptr, cpu_env, +                                                   offsetof(CPUSPARCState, hstick)); +                                    gen_helper_tick_set_limit(r_tickptr, +                                                              cpu_hstick_cmpr); +                                    tcg_temp_free_ptr(r_tickptr); +                                } +                                break; +                            case 6: // hver readonly +                            default: +                                goto illegal_insn; +                            } +#endif +                        } +                        break; +#endif +#ifdef TARGET_SPARC64 +                    case 0x2c: /* V9 movcc */ +                        { +                            int cc = GET_FIELD_SP(insn, 11, 12); +                            int cond = GET_FIELD_SP(insn, 14, 17); +                            DisasCompare cmp; +                            TCGv dst; + +                            if (insn & (1 << 18)) { +                                if (cc == 0) { +                                    gen_compare(&cmp, 0, cond, dc); +                                } else if (cc == 2) { +                                    gen_compare(&cmp, 1, cond, dc); +                                } else { +                                    goto illegal_insn; +                                } +                            } else { +                                gen_fcompare(&cmp, cc, cond); +                            } + +                            /* The get_src2 above loaded the normal 13-bit +                               immediate field, not the 11-bit field we have +                               in movcc.  But it did handle the reg case.  */ +                            if (IS_IMM) { +                                simm = GET_FIELD_SPs(insn, 0, 10); +                                tcg_gen_movi_tl(cpu_src2, simm); +                            } + +                            dst = gen_load_gpr(dc, rd); +                            tcg_gen_movcond_tl(cmp.cond, dst, +                                               cmp.c1, cmp.c2, +                                               cpu_src2, dst); +                            free_compare(&cmp); +                            gen_store_gpr(dc, rd, dst); +                            break; +                        } +                    case 0x2d: /* V9 sdivx */ +                        gen_helper_sdivx(cpu_dst, cpu_env, cpu_src1, cpu_src2); +                        gen_store_gpr(dc, rd, cpu_dst); +                        break; +                    case 0x2e: /* V9 popc */ +                        gen_helper_popc(cpu_dst, cpu_src2); +                        gen_store_gpr(dc, rd, cpu_dst); +                        break; +                    case 0x2f: /* V9 movr */ +                        { +                            int cond = GET_FIELD_SP(insn, 10, 12); +                            DisasCompare cmp; +                            TCGv dst; + +                            gen_compare_reg(&cmp, cond, cpu_src1); + +                            /* The get_src2 above loaded the normal 13-bit +                               immediate field, not the 10-bit field we have +                               in movr.  But it did handle the reg case.  */ +                            if (IS_IMM) { +                                simm = GET_FIELD_SPs(insn, 0, 9); +                                tcg_gen_movi_tl(cpu_src2, simm); +                            } + +                            dst = gen_load_gpr(dc, rd); +                            tcg_gen_movcond_tl(cmp.cond, dst, +                                               cmp.c1, cmp.c2, +                                               cpu_src2, dst); +                            free_compare(&cmp); +                            gen_store_gpr(dc, rd, dst); +                            break; +                        } +#endif +                    default: +                        goto illegal_insn; +                    } +                } +            } else if (xop == 0x36) { /* UltraSparc shutdown, VIS, V8 CPop1 */ +#ifdef TARGET_SPARC64 +                int opf = GET_FIELD_SP(insn, 5, 13); +                rs1 = GET_FIELD(insn, 13, 17); +                rs2 = GET_FIELD(insn, 27, 31); +                if (gen_trap_ifnofpu(dc)) { +                    goto jmp_insn; +                } + +                switch (opf) { +                case 0x000: /* VIS I edge8cc */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 1, 0); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x001: /* VIS II edge8n */ +                    CHECK_FPU_FEATURE(dc, VIS2); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 0, 0); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x002: /* VIS I edge8lcc */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 1, 1); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x003: /* VIS II edge8ln */ +                    CHECK_FPU_FEATURE(dc, VIS2); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 8, 0, 1); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x004: /* VIS I edge16cc */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 1, 0); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x005: /* VIS II edge16n */ +                    CHECK_FPU_FEATURE(dc, VIS2); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 0, 0); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x006: /* VIS I edge16lcc */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 1, 1); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x007: /* VIS II edge16ln */ +                    CHECK_FPU_FEATURE(dc, VIS2); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 16, 0, 1); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x008: /* VIS I edge32cc */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 1, 0); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x009: /* VIS II edge32n */ +                    CHECK_FPU_FEATURE(dc, VIS2); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 0, 0); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x00a: /* VIS I edge32lcc */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 1, 1); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x00b: /* VIS II edge32ln */ +                    CHECK_FPU_FEATURE(dc, VIS2); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_edge(dc, cpu_dst, cpu_src1, cpu_src2, 32, 0, 1); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x010: /* VIS I array8 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_helper_array8(cpu_dst, cpu_src1, cpu_src2); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x012: /* VIS I array16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_helper_array8(cpu_dst, cpu_src1, cpu_src2); +                    tcg_gen_shli_i64(cpu_dst, cpu_dst, 1); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x014: /* VIS I array32 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_helper_array8(cpu_dst, cpu_src1, cpu_src2); +                    tcg_gen_shli_i64(cpu_dst, cpu_dst, 2); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x018: /* VIS I alignaddr */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_alignaddr(cpu_dst, cpu_src1, cpu_src2, 0); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x01a: /* VIS I alignaddrl */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_alignaddr(cpu_dst, cpu_src1, cpu_src2, 1); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x019: /* VIS II bmask */ +                    CHECK_FPU_FEATURE(dc, VIS2); +                    cpu_src1 = gen_load_gpr(dc, rs1); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    tcg_gen_add_tl(cpu_dst, cpu_src1, cpu_src2); +                    tcg_gen_deposit_tl(cpu_gsr, cpu_gsr, cpu_dst, 32, 32); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x020: /* VIS I fcmple16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                    cpu_src2_64 = gen_load_fpr_D(dc, rs2); +                    gen_helper_fcmple16(cpu_dst, cpu_src1_64, cpu_src2_64); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x022: /* VIS I fcmpne16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                    cpu_src2_64 = gen_load_fpr_D(dc, rs2); +                    gen_helper_fcmpne16(cpu_dst, cpu_src1_64, cpu_src2_64); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x024: /* VIS I fcmple32 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                    cpu_src2_64 = gen_load_fpr_D(dc, rs2); +                    gen_helper_fcmple32(cpu_dst, cpu_src1_64, cpu_src2_64); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x026: /* VIS I fcmpne32 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                    cpu_src2_64 = gen_load_fpr_D(dc, rs2); +                    gen_helper_fcmpne32(cpu_dst, cpu_src1_64, cpu_src2_64); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x028: /* VIS I fcmpgt16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                    cpu_src2_64 = gen_load_fpr_D(dc, rs2); +                    gen_helper_fcmpgt16(cpu_dst, cpu_src1_64, cpu_src2_64); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x02a: /* VIS I fcmpeq16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                    cpu_src2_64 = gen_load_fpr_D(dc, rs2); +                    gen_helper_fcmpeq16(cpu_dst, cpu_src1_64, cpu_src2_64); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x02c: /* VIS I fcmpgt32 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                    cpu_src2_64 = gen_load_fpr_D(dc, rs2); +                    gen_helper_fcmpgt32(cpu_dst, cpu_src1_64, cpu_src2_64); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x02e: /* VIS I fcmpeq32 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                    cpu_src2_64 = gen_load_fpr_D(dc, rs2); +                    gen_helper_fcmpeq32(cpu_dst, cpu_src1_64, cpu_src2_64); +                    gen_store_gpr(dc, rd, cpu_dst); +                    break; +                case 0x031: /* VIS I fmul8x16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16); +                    break; +                case 0x033: /* VIS I fmul8x16au */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16au); +                    break; +                case 0x035: /* VIS I fmul8x16al */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8x16al); +                    break; +                case 0x036: /* VIS I fmul8sux16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8sux16); +                    break; +                case 0x037: /* VIS I fmul8ulx16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmul8ulx16); +                    break; +                case 0x038: /* VIS I fmuld8sux16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld8sux16); +                    break; +                case 0x039: /* VIS I fmuld8ulx16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fmuld8ulx16); +                    break; +                case 0x03a: /* VIS I fpack32 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpack32); +                    break; +                case 0x03b: /* VIS I fpack16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs2); +                    cpu_dst_32 = gen_dest_fpr_F(dc); +                    gen_helper_fpack16(cpu_dst_32, cpu_gsr, cpu_src1_64); +                    gen_store_fpr_F(dc, rd, cpu_dst_32); +                    break; +                case 0x03d: /* VIS I fpackfix */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs2); +                    cpu_dst_32 = gen_dest_fpr_F(dc); +                    gen_helper_fpackfix(cpu_dst_32, cpu_gsr, cpu_src1_64); +                    gen_store_fpr_F(dc, rd, cpu_dst_32); +                    break; +                case 0x03e: /* VIS I pdist */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDDD(dc, rd, rs1, rs2, gen_helper_pdist); +                    break; +                case 0x048: /* VIS I faligndata */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_faligndata); +                    break; +                case 0x04b: /* VIS I fpmerge */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpmerge); +                    break; +                case 0x04c: /* VIS II bshuffle */ +                    CHECK_FPU_FEATURE(dc, VIS2); +                    gen_gsr_fop_DDD(dc, rd, rs1, rs2, gen_helper_bshuffle); +                    break; +                case 0x04d: /* VIS I fexpand */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fexpand); +                    break; +                case 0x050: /* VIS I fpadd16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpadd16); +                    break; +                case 0x051: /* VIS I fpadd16s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, gen_helper_fpadd16s); +                    break; +                case 0x052: /* VIS I fpadd32 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpadd32); +                    break; +                case 0x053: /* VIS I fpadd32s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_add_i32); +                    break; +                case 0x054: /* VIS I fpsub16 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpsub16); +                    break; +                case 0x055: /* VIS I fpsub16s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, gen_helper_fpsub16s); +                    break; +                case 0x056: /* VIS I fpsub32 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, gen_helper_fpsub32); +                    break; +                case 0x057: /* VIS I fpsub32s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_sub_i32); +                    break; +                case 0x060: /* VIS I fzero */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_dst_64 = gen_dest_fpr_D(dc, rd); +                    tcg_gen_movi_i64(cpu_dst_64, 0); +                    gen_store_fpr_D(dc, rd, cpu_dst_64); +                    break; +                case 0x061: /* VIS I fzeros */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_dst_32 = gen_dest_fpr_F(dc); +                    tcg_gen_movi_i32(cpu_dst_32, 0); +                    gen_store_fpr_F(dc, rd, cpu_dst_32); +                    break; +                case 0x062: /* VIS I fnor */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_nor_i64); +                    break; +                case 0x063: /* VIS I fnors */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_nor_i32); +                    break; +                case 0x064: /* VIS I fandnot2 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_andc_i64); +                    break; +                case 0x065: /* VIS I fandnot2s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_andc_i32); +                    break; +                case 0x066: /* VIS I fnot2 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DD(dc, rd, rs2, tcg_gen_not_i64); +                    break; +                case 0x067: /* VIS I fnot2s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FF(dc, rd, rs2, tcg_gen_not_i32); +                    break; +                case 0x068: /* VIS I fandnot1 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_andc_i64); +                    break; +                case 0x069: /* VIS I fandnot1s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs2, rs1, tcg_gen_andc_i32); +                    break; +                case 0x06a: /* VIS I fnot1 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DD(dc, rd, rs1, tcg_gen_not_i64); +                    break; +                case 0x06b: /* VIS I fnot1s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FF(dc, rd, rs1, tcg_gen_not_i32); +                    break; +                case 0x06c: /* VIS I fxor */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_xor_i64); +                    break; +                case 0x06d: /* VIS I fxors */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_xor_i32); +                    break; +                case 0x06e: /* VIS I fnand */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_nand_i64); +                    break; +                case 0x06f: /* VIS I fnands */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_nand_i32); +                    break; +                case 0x070: /* VIS I fand */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_and_i64); +                    break; +                case 0x071: /* VIS I fands */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_and_i32); +                    break; +                case 0x072: /* VIS I fxnor */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_eqv_i64); +                    break; +                case 0x073: /* VIS I fxnors */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_eqv_i32); +                    break; +                case 0x074: /* VIS I fsrc1 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs1); +                    gen_store_fpr_D(dc, rd, cpu_src1_64); +                    break; +                case 0x075: /* VIS I fsrc1s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_32 = gen_load_fpr_F(dc, rs1); +                    gen_store_fpr_F(dc, rd, cpu_src1_32); +                    break; +                case 0x076: /* VIS I fornot2 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_orc_i64); +                    break; +                case 0x077: /* VIS I fornot2s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_orc_i32); +                    break; +                case 0x078: /* VIS I fsrc2 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_64 = gen_load_fpr_D(dc, rs2); +                    gen_store_fpr_D(dc, rd, cpu_src1_64); +                    break; +                case 0x079: /* VIS I fsrc2s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_src1_32 = gen_load_fpr_F(dc, rs2); +                    gen_store_fpr_F(dc, rd, cpu_src1_32); +                    break; +                case 0x07a: /* VIS I fornot1 */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs2, rs1, tcg_gen_orc_i64); +                    break; +                case 0x07b: /* VIS I fornot1s */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs2, rs1, tcg_gen_orc_i32); +                    break; +                case 0x07c: /* VIS I for */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_DDD(dc, rd, rs1, rs2, tcg_gen_or_i64); +                    break; +                case 0x07d: /* VIS I fors */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    gen_ne_fop_FFF(dc, rd, rs1, rs2, tcg_gen_or_i32); +                    break; +                case 0x07e: /* VIS I fone */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_dst_64 = gen_dest_fpr_D(dc, rd); +                    tcg_gen_movi_i64(cpu_dst_64, -1); +                    gen_store_fpr_D(dc, rd, cpu_dst_64); +                    break; +                case 0x07f: /* VIS I fones */ +                    CHECK_FPU_FEATURE(dc, VIS1); +                    cpu_dst_32 = gen_dest_fpr_F(dc); +                    tcg_gen_movi_i32(cpu_dst_32, -1); +                    gen_store_fpr_F(dc, rd, cpu_dst_32); +                    break; +                case 0x080: /* VIS I shutdown */ +                case 0x081: /* VIS II siam */ +                    // XXX +                    goto illegal_insn; +                default: +                    goto illegal_insn; +                } +#else +                goto ncp_insn; +#endif +            } else if (xop == 0x37) { /* V8 CPop2, V9 impdep2 */ +#ifdef TARGET_SPARC64 +                goto illegal_insn; +#else +                goto ncp_insn; +#endif +#ifdef TARGET_SPARC64 +            } else if (xop == 0x39) { /* V9 return */ +                TCGv_i32 r_const; + +                save_state(dc); +                cpu_src1 = get_src1(dc, insn); +                cpu_tmp0 = get_temp_tl(dc); +                if (IS_IMM) {   /* immediate */ +                    simm = GET_FIELDs(insn, 19, 31); +                    tcg_gen_addi_tl(cpu_tmp0, cpu_src1, simm); +                } else {                /* register */ +                    rs2 = GET_FIELD(insn, 27, 31); +                    if (rs2) { +                        cpu_src2 = gen_load_gpr(dc, rs2); +                        tcg_gen_add_tl(cpu_tmp0, cpu_src1, cpu_src2); +                    } else { +                        tcg_gen_mov_tl(cpu_tmp0, cpu_src1); +                    } +                } +                gen_helper_restore(cpu_env); +                gen_mov_pc_npc(dc); +                r_const = tcg_const_i32(3); +                gen_helper_check_align(cpu_env, cpu_tmp0, r_const); +                tcg_temp_free_i32(r_const); +                tcg_gen_mov_tl(cpu_npc, cpu_tmp0); +                dc->npc = DYNAMIC_PC; +                goto jmp_insn; +#endif +            } else { +                cpu_src1 = get_src1(dc, insn); +                cpu_tmp0 = get_temp_tl(dc); +                if (IS_IMM) {   /* immediate */ +                    simm = GET_FIELDs(insn, 19, 31); +                    tcg_gen_addi_tl(cpu_tmp0, cpu_src1, simm); +                } else {                /* register */ +                    rs2 = GET_FIELD(insn, 27, 31); +                    if (rs2) { +                        cpu_src2 = gen_load_gpr(dc, rs2); +                        tcg_gen_add_tl(cpu_tmp0, cpu_src1, cpu_src2); +                    } else { +                        tcg_gen_mov_tl(cpu_tmp0, cpu_src1); +                    } +                } +                switch (xop) { +                case 0x38:      /* jmpl */ +                    { +                        TCGv t; +                        TCGv_i32 r_const; + +                        t = gen_dest_gpr(dc, rd); +                        tcg_gen_movi_tl(t, dc->pc); +                        gen_store_gpr(dc, rd, t); +                        gen_mov_pc_npc(dc); +                        r_const = tcg_const_i32(3); +                        gen_helper_check_align(cpu_env, cpu_tmp0, r_const); +                        tcg_temp_free_i32(r_const); +                        gen_address_mask(dc, cpu_tmp0); +                        tcg_gen_mov_tl(cpu_npc, cpu_tmp0); +                        dc->npc = DYNAMIC_PC; +                    } +                    goto jmp_insn; +#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) +                case 0x39:      /* rett, V9 return */ +                    { +                        TCGv_i32 r_const; + +                        if (!supervisor(dc)) +                            goto priv_insn; +                        gen_mov_pc_npc(dc); +                        r_const = tcg_const_i32(3); +                        gen_helper_check_align(cpu_env, cpu_tmp0, r_const); +                        tcg_temp_free_i32(r_const); +                        tcg_gen_mov_tl(cpu_npc, cpu_tmp0); +                        dc->npc = DYNAMIC_PC; +                        gen_helper_rett(cpu_env); +                    } +                    goto jmp_insn; +#endif +                case 0x3b: /* flush */ +                    if (!((dc)->def->features & CPU_FEATURE_FLUSH)) +                        goto unimp_flush; +                    /* nop */ +                    break; +                case 0x3c:      /* save */ +                    save_state(dc); +                    gen_helper_save(cpu_env); +                    gen_store_gpr(dc, rd, cpu_tmp0); +                    break; +                case 0x3d:      /* restore */ +                    save_state(dc); +                    gen_helper_restore(cpu_env); +                    gen_store_gpr(dc, rd, cpu_tmp0); +                    break; +#if !defined(CONFIG_USER_ONLY) && defined(TARGET_SPARC64) +                case 0x3e:      /* V9 done/retry */ +                    { +                        switch (rd) { +                        case 0: +                            if (!supervisor(dc)) +                                goto priv_insn; +                            dc->npc = DYNAMIC_PC; +                            dc->pc = DYNAMIC_PC; +                            gen_helper_done(cpu_env); +                            goto jmp_insn; +                        case 1: +                            if (!supervisor(dc)) +                                goto priv_insn; +                            dc->npc = DYNAMIC_PC; +                            dc->pc = DYNAMIC_PC; +                            gen_helper_retry(cpu_env); +                            goto jmp_insn; +                        default: +                            goto illegal_insn; +                        } +                    } +                    break; +#endif +                default: +                    goto illegal_insn; +                } +            } +            break; +        } +        break; +    case 3:                     /* load/store instructions */ +        { +            unsigned int xop = GET_FIELD(insn, 7, 12); +            /* ??? gen_address_mask prevents us from using a source +               register directly.  Always generate a temporary.  */ +            TCGv cpu_addr = get_temp_tl(dc); + +            tcg_gen_mov_tl(cpu_addr, get_src1(dc, insn)); +            if (xop == 0x3c || xop == 0x3e) { +                /* V9 casa/casxa : no offset */ +            } else if (IS_IMM) {     /* immediate */ +                simm = GET_FIELDs(insn, 19, 31); +                if (simm != 0) { +                    tcg_gen_addi_tl(cpu_addr, cpu_addr, simm); +                } +            } else {            /* register */ +                rs2 = GET_FIELD(insn, 27, 31); +                if (rs2 != 0) { +                    tcg_gen_add_tl(cpu_addr, cpu_addr, gen_load_gpr(dc, rs2)); +                } +            } +            if (xop < 4 || (xop > 7 && xop < 0x14 && xop != 0x0e) || +                (xop > 0x17 && xop <= 0x1d ) || +                (xop > 0x2c && xop <= 0x33) || xop == 0x1f || xop == 0x3d) { +                TCGv cpu_val = gen_dest_gpr(dc, rd); + +                switch (xop) { +                case 0x0:       /* ld, V9 lduw, load unsigned word */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_ld32u(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0x1:       /* ldub, load unsigned byte */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_ld8u(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0x2:       /* lduh, load unsigned halfword */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_ld16u(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0x3:       /* ldd, load double word */ +                    if (rd & 1) +                        goto illegal_insn; +                    else { +                        TCGv_i32 r_const; +                        TCGv_i64 t64; + +                        save_state(dc); +                        r_const = tcg_const_i32(7); +                        /* XXX remove alignment check */ +                        gen_helper_check_align(cpu_env, cpu_addr, r_const); +                        tcg_temp_free_i32(r_const); +                        gen_address_mask(dc, cpu_addr); +                        t64 = tcg_temp_new_i64(); +                        tcg_gen_qemu_ld64(t64, cpu_addr, dc->mem_idx); +                        tcg_gen_trunc_i64_tl(cpu_val, t64); +                        tcg_gen_ext32u_tl(cpu_val, cpu_val); +                        gen_store_gpr(dc, rd + 1, cpu_val); +                        tcg_gen_shri_i64(t64, t64, 32); +                        tcg_gen_trunc_i64_tl(cpu_val, t64); +                        tcg_temp_free_i64(t64); +                        tcg_gen_ext32u_tl(cpu_val, cpu_val); +                    } +                    break; +                case 0x9:       /* ldsb, load signed byte */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_ld8s(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0xa:       /* ldsh, load signed halfword */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_ld16s(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0xd:       /* ldstub -- XXX: should be atomically */ +                    { +                        TCGv r_const; + +                        gen_address_mask(dc, cpu_addr); +                        tcg_gen_qemu_ld8s(cpu_val, cpu_addr, dc->mem_idx); +                        r_const = tcg_const_tl(0xff); +                        tcg_gen_qemu_st8(r_const, cpu_addr, dc->mem_idx); +                        tcg_temp_free(r_const); +                    } +                    break; +                case 0x0f: +                    /* swap, swap register with memory. Also atomically */ +                    { +                        TCGv t0 = get_temp_tl(dc); +                        CHECK_IU_FEATURE(dc, SWAP); +                        cpu_src1 = gen_load_gpr(dc, rd); +                        gen_address_mask(dc, cpu_addr); +                        tcg_gen_qemu_ld32u(t0, cpu_addr, dc->mem_idx); +                        tcg_gen_qemu_st32(cpu_src1, cpu_addr, dc->mem_idx); +                        tcg_gen_mov_tl(cpu_val, t0); +                    } +                    break; +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) +                case 0x10:      /* lda, V9 lduwa, load word alternate */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    save_state(dc); +                    gen_ld_asi(cpu_val, cpu_addr, insn, 4, 0); +                    break; +                case 0x11:      /* lduba, load unsigned byte alternate */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    save_state(dc); +                    gen_ld_asi(cpu_val, cpu_addr, insn, 1, 0); +                    break; +                case 0x12:      /* lduha, load unsigned halfword alternate */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    save_state(dc); +                    gen_ld_asi(cpu_val, cpu_addr, insn, 2, 0); +                    break; +                case 0x13:      /* ldda, load double word alternate */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    if (rd & 1) +                        goto illegal_insn; +                    save_state(dc); +                    gen_ldda_asi(dc, cpu_val, cpu_addr, insn, rd); +                    goto skip_move; +                case 0x19:      /* ldsba, load signed byte alternate */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    save_state(dc); +                    gen_ld_asi(cpu_val, cpu_addr, insn, 1, 1); +                    break; +                case 0x1a:      /* ldsha, load signed halfword alternate */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    save_state(dc); +                    gen_ld_asi(cpu_val, cpu_addr, insn, 2, 1); +                    break; +                case 0x1d:      /* ldstuba -- XXX: should be atomically */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    save_state(dc); +                    gen_ldstub_asi(cpu_val, cpu_addr, insn); +                    break; +                case 0x1f:      /* swapa, swap reg with alt. memory. Also +                                   atomically */ +                    CHECK_IU_FEATURE(dc, SWAP); +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    save_state(dc); +                    cpu_src1 = gen_load_gpr(dc, rd); +                    gen_swap_asi(cpu_val, cpu_src1, cpu_addr, insn); +                    break; + +#ifndef TARGET_SPARC64 +                case 0x30: /* ldc */ +                case 0x31: /* ldcsr */ +                case 0x33: /* lddc */ +                    goto ncp_insn; +#endif +#endif +#ifdef TARGET_SPARC64 +                case 0x08: /* V9 ldsw */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_ld32s(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0x0b: /* V9 ldx */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_ld64(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0x18: /* V9 ldswa */ +                    save_state(dc); +                    gen_ld_asi(cpu_val, cpu_addr, insn, 4, 1); +                    break; +                case 0x1b: /* V9 ldxa */ +                    save_state(dc); +                    gen_ld_asi(cpu_val, cpu_addr, insn, 8, 0); +                    break; +                case 0x2d: /* V9 prefetch, no effect */ +                    goto skip_move; +                case 0x30: /* V9 ldfa */ +                    if (gen_trap_ifnofpu(dc)) { +                        goto jmp_insn; +                    } +                    save_state(dc); +                    gen_ldf_asi(cpu_addr, insn, 4, rd); +                    gen_update_fprs_dirty(rd); +                    goto skip_move; +                case 0x33: /* V9 lddfa */ +                    if (gen_trap_ifnofpu(dc)) { +                        goto jmp_insn; +                    } +                    save_state(dc); +                    gen_ldf_asi(cpu_addr, insn, 8, DFPREG(rd)); +                    gen_update_fprs_dirty(DFPREG(rd)); +                    goto skip_move; +                case 0x3d: /* V9 prefetcha, no effect */ +                    goto skip_move; +                case 0x32: /* V9 ldqfa */ +                    CHECK_FPU_FEATURE(dc, FLOAT128); +                    if (gen_trap_ifnofpu(dc)) { +                        goto jmp_insn; +                    } +                    save_state(dc); +                    gen_ldf_asi(cpu_addr, insn, 16, QFPREG(rd)); +                    gen_update_fprs_dirty(QFPREG(rd)); +                    goto skip_move; +#endif +                default: +                    goto illegal_insn; +                } +                gen_store_gpr(dc, rd, cpu_val); +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) +            skip_move: ; +#endif +            } else if (xop >= 0x20 && xop < 0x24) { +                TCGv t0; + +                if (gen_trap_ifnofpu(dc)) { +                    goto jmp_insn; +                } +                save_state(dc); +                switch (xop) { +                case 0x20:      /* ldf, load fpreg */ +                    gen_address_mask(dc, cpu_addr); +                    t0 = get_temp_tl(dc); +                    tcg_gen_qemu_ld32u(t0, cpu_addr, dc->mem_idx); +                    cpu_dst_32 = gen_dest_fpr_F(dc); +                    tcg_gen_trunc_tl_i32(cpu_dst_32, t0); +                    gen_store_fpr_F(dc, rd, cpu_dst_32); +                    break; +                case 0x21:      /* ldfsr, V9 ldxfsr */ +#ifdef TARGET_SPARC64 +                    gen_address_mask(dc, cpu_addr); +                    if (rd == 1) { +                        TCGv_i64 t64 = tcg_temp_new_i64(); +                        tcg_gen_qemu_ld64(t64, cpu_addr, dc->mem_idx); +                        gen_helper_ldxfsr(cpu_env, t64); +                        tcg_temp_free_i64(t64); +                        break; +                    } +#endif +                    cpu_dst_32 = get_temp_i32(dc); +                    t0 = get_temp_tl(dc); +                    tcg_gen_qemu_ld32u(t0, cpu_addr, dc->mem_idx); +                    tcg_gen_trunc_tl_i32(cpu_dst_32, t0); +                    gen_helper_ldfsr(cpu_env, cpu_dst_32); +                    break; +                case 0x22:      /* ldqf, load quad fpreg */ +                    { +                        TCGv_i32 r_const; + +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        r_const = tcg_const_i32(dc->mem_idx); +                        gen_address_mask(dc, cpu_addr); +                        gen_helper_ldqf(cpu_env, cpu_addr, r_const); +                        tcg_temp_free_i32(r_const); +                        gen_op_store_QT0_fpr(QFPREG(rd)); +                        gen_update_fprs_dirty(QFPREG(rd)); +                    } +                    break; +                case 0x23:      /* lddf, load double fpreg */ +                    gen_address_mask(dc, cpu_addr); +                    cpu_dst_64 = gen_dest_fpr_D(dc, rd); +                    tcg_gen_qemu_ld64(cpu_dst_64, cpu_addr, dc->mem_idx); +                    gen_store_fpr_D(dc, rd, cpu_dst_64); +                    break; +                default: +                    goto illegal_insn; +                } +            } else if (xop < 8 || (xop >= 0x14 && xop < 0x18) || +                       xop == 0xe || xop == 0x1e) { +                TCGv cpu_val = gen_load_gpr(dc, rd); + +                switch (xop) { +                case 0x4: /* st, store word */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_st32(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0x5: /* stb, store byte */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_st8(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0x6: /* sth, store halfword */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_st16(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0x7: /* std, store double word */ +                    if (rd & 1) +                        goto illegal_insn; +                    else { +                        TCGv_i32 r_const; +                        TCGv_i64 t64; +                        TCGv lo; + +                        save_state(dc); +                        gen_address_mask(dc, cpu_addr); +                        r_const = tcg_const_i32(7); +                        /* XXX remove alignment check */ +                        gen_helper_check_align(cpu_env, cpu_addr, r_const); +                        tcg_temp_free_i32(r_const); +                        lo = gen_load_gpr(dc, rd + 1); + +                        t64 = tcg_temp_new_i64(); +                        tcg_gen_concat_tl_i64(t64, lo, cpu_val); +                        tcg_gen_qemu_st64(t64, cpu_addr, dc->mem_idx); +                        tcg_temp_free_i64(t64); +                    } +                    break; +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) +                case 0x14: /* sta, V9 stwa, store word alternate */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    save_state(dc); +                    gen_st_asi(cpu_val, cpu_addr, insn, 4); +                    dc->npc = DYNAMIC_PC; +                    break; +                case 0x15: /* stba, store byte alternate */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    save_state(dc); +                    gen_st_asi(cpu_val, cpu_addr, insn, 1); +                    dc->npc = DYNAMIC_PC; +                    break; +                case 0x16: /* stha, store halfword alternate */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    save_state(dc); +                    gen_st_asi(cpu_val, cpu_addr, insn, 2); +                    dc->npc = DYNAMIC_PC; +                    break; +                case 0x17: /* stda, store double word alternate */ +#ifndef TARGET_SPARC64 +                    if (IS_IMM) +                        goto illegal_insn; +                    if (!supervisor(dc)) +                        goto priv_insn; +#endif +                    if (rd & 1) +                        goto illegal_insn; +                    else { +                        save_state(dc); +                        gen_stda_asi(dc, cpu_val, cpu_addr, insn, rd); +                    } +                    break; +#endif +#ifdef TARGET_SPARC64 +                case 0x0e: /* V9 stx */ +                    gen_address_mask(dc, cpu_addr); +                    tcg_gen_qemu_st64(cpu_val, cpu_addr, dc->mem_idx); +                    break; +                case 0x1e: /* V9 stxa */ +                    save_state(dc); +                    gen_st_asi(cpu_val, cpu_addr, insn, 8); +                    dc->npc = DYNAMIC_PC; +                    break; +#endif +                default: +                    goto illegal_insn; +                } +            } else if (xop > 0x23 && xop < 0x28) { +                if (gen_trap_ifnofpu(dc)) { +                    goto jmp_insn; +                } +                save_state(dc); +                switch (xop) { +                case 0x24: /* stf, store fpreg */ +                    { +                        TCGv t = get_temp_tl(dc); +                        gen_address_mask(dc, cpu_addr); +                        cpu_src1_32 = gen_load_fpr_F(dc, rd); +                        tcg_gen_ext_i32_tl(t, cpu_src1_32); +                        tcg_gen_qemu_st32(t, cpu_addr, dc->mem_idx); +                    } +                    break; +                case 0x25: /* stfsr, V9 stxfsr */ +                    { +                        TCGv t = get_temp_tl(dc); + +                        tcg_gen_ld_tl(t, cpu_env, offsetof(CPUSPARCState, fsr)); +#ifdef TARGET_SPARC64 +                        gen_address_mask(dc, cpu_addr); +                        if (rd == 1) { +                            tcg_gen_qemu_st64(t, cpu_addr, dc->mem_idx); +                            break; +                        } +#endif +                        tcg_gen_qemu_st32(t, cpu_addr, dc->mem_idx); +                    } +                    break; +                case 0x26: +#ifdef TARGET_SPARC64 +                    /* V9 stqf, store quad fpreg */ +                    { +                        TCGv_i32 r_const; + +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        gen_op_load_fpr_QT0(QFPREG(rd)); +                        r_const = tcg_const_i32(dc->mem_idx); +                        gen_address_mask(dc, cpu_addr); +                        gen_helper_stqf(cpu_env, cpu_addr, r_const); +                        tcg_temp_free_i32(r_const); +                    } +                    break; +#else /* !TARGET_SPARC64 */ +                    /* stdfq, store floating point queue */ +#if defined(CONFIG_USER_ONLY) +                    goto illegal_insn; +#else +                    if (!supervisor(dc)) +                        goto priv_insn; +                    if (gen_trap_ifnofpu(dc)) { +                        goto jmp_insn; +                    } +                    goto nfq_insn; +#endif +#endif +                case 0x27: /* stdf, store double fpreg */ +                    gen_address_mask(dc, cpu_addr); +                    cpu_src1_64 = gen_load_fpr_D(dc, rd); +                    tcg_gen_qemu_st64(cpu_src1_64, cpu_addr, dc->mem_idx); +                    break; +                default: +                    goto illegal_insn; +                } +            } else if (xop > 0x33 && xop < 0x3f) { +                save_state(dc); +                switch (xop) { +#ifdef TARGET_SPARC64 +                case 0x34: /* V9 stfa */ +                    if (gen_trap_ifnofpu(dc)) { +                        goto jmp_insn; +                    } +                    gen_stf_asi(cpu_addr, insn, 4, rd); +                    break; +                case 0x36: /* V9 stqfa */ +                    { +                        TCGv_i32 r_const; + +                        CHECK_FPU_FEATURE(dc, FLOAT128); +                        if (gen_trap_ifnofpu(dc)) { +                            goto jmp_insn; +                        } +                        r_const = tcg_const_i32(7); +                        gen_helper_check_align(cpu_env, cpu_addr, r_const); +                        tcg_temp_free_i32(r_const); +                        gen_stf_asi(cpu_addr, insn, 16, QFPREG(rd)); +                    } +                    break; +                case 0x37: /* V9 stdfa */ +                    if (gen_trap_ifnofpu(dc)) { +                        goto jmp_insn; +                    } +                    gen_stf_asi(cpu_addr, insn, 8, DFPREG(rd)); +                    break; +                case 0x3e: /* V9 casxa */ +                    rs2 = GET_FIELD(insn, 27, 31); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_casx_asi(dc, cpu_addr, cpu_src2, insn, rd); +                    break; +#else +                case 0x34: /* stc */ +                case 0x35: /* stcsr */ +                case 0x36: /* stdcq */ +                case 0x37: /* stdc */ +                    goto ncp_insn; +#endif +#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64) +                case 0x3c: /* V9 or LEON3 casa */ +#ifndef TARGET_SPARC64 +                    CHECK_IU_FEATURE(dc, CASA); +                    if (IS_IMM) { +                        goto illegal_insn; +                    } +                    if (!supervisor(dc)) { +                        goto priv_insn; +                    } +#endif +                    rs2 = GET_FIELD(insn, 27, 31); +                    cpu_src2 = gen_load_gpr(dc, rs2); +                    gen_cas_asi(dc, cpu_addr, cpu_src2, insn, rd); +                    break; +#endif +                default: +                    goto illegal_insn; +                } +            } else { +                goto illegal_insn; +            } +        } +        break; +    } +    /* default case for non jump instructions */ +    if (dc->npc == DYNAMIC_PC) { +        dc->pc = DYNAMIC_PC; +        gen_op_next_insn(); +    } else if (dc->npc == JUMP_PC) { +        /* we can do a static jump */ +        gen_branch2(dc, dc->jump_pc[0], dc->jump_pc[1], cpu_cond); +        dc->is_br = 1; +    } else { +        dc->pc = dc->npc; +        dc->npc = dc->npc + 4; +    } + jmp_insn: +    goto egress; + illegal_insn: +    { +        TCGv_i32 r_const; + +        save_state(dc); +        r_const = tcg_const_i32(TT_ILL_INSN); +        gen_helper_raise_exception(cpu_env, r_const); +        tcg_temp_free_i32(r_const); +        dc->is_br = 1; +    } +    goto egress; + unimp_flush: +    { +        TCGv_i32 r_const; + +        save_state(dc); +        r_const = tcg_const_i32(TT_UNIMP_FLUSH); +        gen_helper_raise_exception(cpu_env, r_const); +        tcg_temp_free_i32(r_const); +        dc->is_br = 1; +    } +    goto egress; +#if !defined(CONFIG_USER_ONLY) + priv_insn: +    { +        TCGv_i32 r_const; + +        save_state(dc); +        r_const = tcg_const_i32(TT_PRIV_INSN); +        gen_helper_raise_exception(cpu_env, r_const); +        tcg_temp_free_i32(r_const); +        dc->is_br = 1; +    } +    goto egress; +#endif + nfpu_insn: +    save_state(dc); +    gen_op_fpexception_im(FSR_FTT_UNIMPFPOP); +    dc->is_br = 1; +    goto egress; +#if !defined(CONFIG_USER_ONLY) && !defined(TARGET_SPARC64) + nfq_insn: +    save_state(dc); +    gen_op_fpexception_im(FSR_FTT_SEQ_ERROR); +    dc->is_br = 1; +    goto egress; +#endif +#ifndef TARGET_SPARC64 + ncp_insn: +    { +        TCGv r_const; + +        save_state(dc); +        r_const = tcg_const_i32(TT_NCP_INSN); +        gen_helper_raise_exception(cpu_env, r_const); +        tcg_temp_free(r_const); +        dc->is_br = 1; +    } +    goto egress; +#endif + egress: +    if (dc->n_t32 != 0) { +        int i; +        for (i = dc->n_t32 - 1; i >= 0; --i) { +            tcg_temp_free_i32(dc->t32[i]); +        } +        dc->n_t32 = 0; +    } +    if (dc->n_ttl != 0) { +        int i; +        for (i = dc->n_ttl - 1; i >= 0; --i) { +            tcg_temp_free(dc->ttl[i]); +        } +        dc->n_ttl = 0; +    } +} + +static inline void gen_intermediate_code_internal(SPARCCPU *cpu, +                                                  TranslationBlock *tb, +                                                  bool spc) +{ +    CPUState *cs = CPU(cpu); +    CPUSPARCState *env = &cpu->env; +    target_ulong pc_start, last_pc; +    DisasContext dc1, *dc = &dc1; +    CPUBreakpoint *bp; +    int j, lj = -1; +    int num_insns; +    int max_insns; +    unsigned int insn; + +    memset(dc, 0, sizeof(DisasContext)); +    dc->tb = tb; +    pc_start = tb->pc; +    dc->pc = pc_start; +    last_pc = dc->pc; +    dc->npc = (target_ulong) tb->cs_base; +    dc->cc_op = CC_OP_DYNAMIC; +    dc->mem_idx = cpu_mmu_index(env); +    dc->def = env->def; +    dc->fpu_enabled = tb_fpu_enabled(tb->flags); +    dc->address_mask_32bit = tb_am_enabled(tb->flags); +    dc->singlestep = (cs->singlestep_enabled || singlestep); + +    num_insns = 0; +    max_insns = tb->cflags & CF_COUNT_MASK; +    if (max_insns == 0) +        max_insns = CF_COUNT_MASK; +    gen_tb_start(tb); +    do { +        if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { +            QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { +                if (bp->pc == dc->pc) { +                    if (dc->pc != pc_start) +                        save_state(dc); +                    gen_helper_debug(cpu_env); +                    tcg_gen_exit_tb(0); +                    dc->is_br = 1; +                    goto exit_gen_loop; +                } +            } +        } +        if (spc) { +            qemu_log("Search PC...\n"); +            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] = dc->pc; +                gen_opc_npc[lj] = dc->npc; +                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(); +        last_pc = dc->pc; +        insn = cpu_ldl_code(env, dc->pc); + +        disas_sparc_insn(dc, insn); +        num_insns++; + +        if (dc->is_br) +            break; +        /* if the next PC is different, we abort now */ +        if (dc->pc != (last_pc + 4)) +            break; +        /* if we reach a page boundary, we stop generation so that the +           PC of a TT_TFAULT exception is always in the right page */ +        if ((dc->pc & (TARGET_PAGE_SIZE - 1)) == 0) +            break; +        /* if single step mode, we generate only one instruction and +           generate an exception */ +        if (dc->singlestep) { +            break; +        } +    } while (!tcg_op_buf_full() && +             (dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32) && +             num_insns < max_insns); + + exit_gen_loop: +    if (tb->cflags & CF_LAST_IO) { +        gen_io_end(); +    } +    if (!dc->is_br) { +        if (dc->pc != DYNAMIC_PC && +            (dc->npc != DYNAMIC_PC && dc->npc != JUMP_PC)) { +            /* static PC and NPC: we can use direct chaining */ +            gen_goto_tb(dc, 0, dc->pc, dc->npc); +        } else { +            if (dc->pc != DYNAMIC_PC) { +                tcg_gen_movi_tl(cpu_pc, dc->pc); +            } +            save_npc(dc); +            tcg_gen_exit_tb(0); +        } +    } +    gen_tb_end(tb, num_insns); + +    if (spc) { +        j = tcg_op_buf_count(); +        lj++; +        while (lj <= j) +            tcg_ctx.gen_opc_instr_start[lj++] = 0; +#if 0 +        log_page_dump(); +#endif +        gen_opc_jump_pc[0] = dc->jump_pc[0]; +        gen_opc_jump_pc[1] = dc->jump_pc[1]; +    } else { +        tb->size = last_pc + 4 - pc_start; +        tb->icount = num_insns; +    } +#ifdef DEBUG_DISAS +    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { +        qemu_log("--------------\n"); +        qemu_log("IN: %s\n", lookup_symbol(pc_start)); +        log_target_disas(cs, pc_start, last_pc + 4 - pc_start, 0); +        qemu_log("\n"); +    } +#endif +} + +void gen_intermediate_code(CPUSPARCState * env, TranslationBlock * tb) +{ +    gen_intermediate_code_internal(sparc_env_get_cpu(env), tb, false); +} + +void gen_intermediate_code_pc(CPUSPARCState * env, TranslationBlock * tb) +{ +    gen_intermediate_code_internal(sparc_env_get_cpu(env), tb, true); +} + +void gen_intermediate_code_init(CPUSPARCState *env) +{ +    unsigned int i; +    static int inited; +    static const char * const gregnames[8] = { +        NULL, // g0 not used +        "g1", +        "g2", +        "g3", +        "g4", +        "g5", +        "g6", +        "g7", +    }; +    static const char * const fregnames[32] = { +        "f0", "f2", "f4", "f6", "f8", "f10", "f12", "f14", +        "f16", "f18", "f20", "f22", "f24", "f26", "f28", "f30", +        "f32", "f34", "f36", "f38", "f40", "f42", "f44", "f46", +        "f48", "f50", "f52", "f54", "f56", "f58", "f60", "f62", +    }; + +    /* init various static tables */ +    if (!inited) { +        inited = 1; + +        cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); +        cpu_regwptr = tcg_global_mem_new_ptr(TCG_AREG0, +                                             offsetof(CPUSPARCState, regwptr), +                                             "regwptr"); +#ifdef TARGET_SPARC64 +        cpu_xcc = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSPARCState, xcc), +                                         "xcc"); +        cpu_asi = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSPARCState, asi), +                                         "asi"); +        cpu_fprs = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSPARCState, fprs), +                                          "fprs"); +        cpu_gsr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, gsr), +                                     "gsr"); +        cpu_tick_cmpr = tcg_global_mem_new(TCG_AREG0, +                                           offsetof(CPUSPARCState, tick_cmpr), +                                           "tick_cmpr"); +        cpu_stick_cmpr = tcg_global_mem_new(TCG_AREG0, +                                            offsetof(CPUSPARCState, stick_cmpr), +                                            "stick_cmpr"); +        cpu_hstick_cmpr = tcg_global_mem_new(TCG_AREG0, +                                             offsetof(CPUSPARCState, hstick_cmpr), +                                             "hstick_cmpr"); +        cpu_hintp = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, hintp), +                                       "hintp"); +        cpu_htba = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, htba), +                                      "htba"); +        cpu_hver = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, hver), +                                      "hver"); +        cpu_ssr = tcg_global_mem_new(TCG_AREG0, +                                     offsetof(CPUSPARCState, ssr), "ssr"); +        cpu_ver = tcg_global_mem_new(TCG_AREG0, +                                     offsetof(CPUSPARCState, version), "ver"); +        cpu_softint = tcg_global_mem_new_i32(TCG_AREG0, +                                             offsetof(CPUSPARCState, softint), +                                             "softint"); +#else +        cpu_wim = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, wim), +                                     "wim"); +#endif +        cpu_cond = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, cond), +                                      "cond"); +        cpu_cc_src = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, cc_src), +                                        "cc_src"); +        cpu_cc_src2 = tcg_global_mem_new(TCG_AREG0, +                                         offsetof(CPUSPARCState, cc_src2), +                                         "cc_src2"); +        cpu_cc_dst = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, cc_dst), +                                        "cc_dst"); +        cpu_cc_op = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSPARCState, cc_op), +                                           "cc_op"); +        cpu_psr = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSPARCState, psr), +                                         "psr"); +        cpu_fsr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, fsr), +                                     "fsr"); +        cpu_pc = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, pc), +                                    "pc"); +        cpu_npc = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, npc), +                                     "npc"); +        cpu_y = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, y), "y"); +#ifndef CONFIG_USER_ONLY +        cpu_tbr = tcg_global_mem_new(TCG_AREG0, offsetof(CPUSPARCState, tbr), +                                     "tbr"); +#endif +        for (i = 1; i < 8; i++) { +            cpu_gregs[i] = tcg_global_mem_new(TCG_AREG0, +                                              offsetof(CPUSPARCState, gregs[i]), +                                              gregnames[i]); +        } +        for (i = 0; i < TARGET_DPREGS; i++) { +            cpu_fpr[i] = tcg_global_mem_new_i64(TCG_AREG0, +                                                offsetof(CPUSPARCState, fpr[i]), +                                                fregnames[i]); +        } +    } +} + +void restore_state_to_opc(CPUSPARCState *env, TranslationBlock *tb, int pc_pos) +{ +    target_ulong npc; +    env->pc = tcg_ctx.gen_opc_pc[pc_pos]; +    npc = gen_opc_npc[pc_pos]; +    if (npc == 1) { +        /* dynamic NPC: already stored */ +    } else if (npc == 2) { +        /* jump PC: use 'cond' and the jump targets of the translation */ +        if (env->cond) { +            env->npc = gen_opc_jump_pc[0]; +        } else { +            env->npc = gen_opc_jump_pc[1]; +        } +    } else { +        env->npc = npc; +    } +} diff --git a/target-sparc/vis_helper.c b/target-sparc/vis_helper.c new file mode 100644 index 00000000..383cc8bd --- /dev/null +++ b/target-sparc/vis_helper.c @@ -0,0 +1,489 @@ +/* + * VIS op helpers + * + *  Copyright (c) 2003-2005 Fabrice Bellard + * + * 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" + +/* This function uses non-native bit order */ +#define GET_FIELD(X, FROM, TO)                                  \ +    ((X) >> (63 - (TO)) & ((1ULL << ((TO) - (FROM) + 1)) - 1)) + +/* This function uses the order in the manuals, i.e. bit 0 is 2^0 */ +#define GET_FIELD_SP(X, FROM, TO)               \ +    GET_FIELD(X, 63 - (TO), 63 - (FROM)) + +target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize) +{ +    return (GET_FIELD_SP(pixel_addr, 60, 63) << (17 + 2 * cubesize)) | +        (GET_FIELD_SP(pixel_addr, 39, 39 + cubesize - 1) << (17 + cubesize)) | +        (GET_FIELD_SP(pixel_addr, 17 + cubesize - 1, 17) << 17) | +        (GET_FIELD_SP(pixel_addr, 56, 59) << 13) | +        (GET_FIELD_SP(pixel_addr, 35, 38) << 9) | +        (GET_FIELD_SP(pixel_addr, 13, 16) << 5) | +        (((pixel_addr >> 55) & 1) << 4) | +        (GET_FIELD_SP(pixel_addr, 33, 34) << 2) | +        GET_FIELD_SP(pixel_addr, 11, 12); +} + +#ifdef HOST_WORDS_BIGENDIAN +#define VIS_B64(n) b[7 - (n)] +#define VIS_W64(n) w[3 - (n)] +#define VIS_SW64(n) sw[3 - (n)] +#define VIS_L64(n) l[1 - (n)] +#define VIS_B32(n) b[3 - (n)] +#define VIS_W32(n) w[1 - (n)] +#else +#define VIS_B64(n) b[n] +#define VIS_W64(n) w[n] +#define VIS_SW64(n) sw[n] +#define VIS_L64(n) l[n] +#define VIS_B32(n) b[n] +#define VIS_W32(n) w[n] +#endif + +typedef union { +    uint8_t b[8]; +    uint16_t w[4]; +    int16_t sw[4]; +    uint32_t l[2]; +    uint64_t ll; +    float64 d; +} VIS64; + +typedef union { +    uint8_t b[4]; +    uint16_t w[2]; +    uint32_t l; +    float32 f; +} VIS32; + +uint64_t helper_fpmerge(uint64_t src1, uint64_t src2) +{ +    VIS64 s, d; + +    s.ll = src1; +    d.ll = src2; + +    /* Reverse calculation order to handle overlap */ +    d.VIS_B64(7) = s.VIS_B64(3); +    d.VIS_B64(6) = d.VIS_B64(3); +    d.VIS_B64(5) = s.VIS_B64(2); +    d.VIS_B64(4) = d.VIS_B64(2); +    d.VIS_B64(3) = s.VIS_B64(1); +    d.VIS_B64(2) = d.VIS_B64(1); +    d.VIS_B64(1) = s.VIS_B64(0); +    /* d.VIS_B64(0) = d.VIS_B64(0); */ + +    return d.ll; +} + +uint64_t helper_fmul8x16(uint64_t src1, uint64_t src2) +{ +    VIS64 s, d; +    uint32_t tmp; + +    s.ll = src1; +    d.ll = src2; + +#define PMUL(r)                                                 \ +    tmp = (int32_t)d.VIS_SW64(r) * (int32_t)s.VIS_B64(r);       \ +    if ((tmp & 0xff) > 0x7f) {                                  \ +        tmp += 0x100;                                           \ +    }                                                           \ +    d.VIS_W64(r) = tmp >> 8; + +    PMUL(0); +    PMUL(1); +    PMUL(2); +    PMUL(3); +#undef PMUL + +    return d.ll; +} + +uint64_t helper_fmul8x16al(uint64_t src1, uint64_t src2) +{ +    VIS64 s, d; +    uint32_t tmp; + +    s.ll = src1; +    d.ll = src2; + +#define PMUL(r)                                                 \ +    tmp = (int32_t)d.VIS_SW64(1) * (int32_t)s.VIS_B64(r);       \ +    if ((tmp & 0xff) > 0x7f) {                                  \ +        tmp += 0x100;                                           \ +    }                                                           \ +    d.VIS_W64(r) = tmp >> 8; + +    PMUL(0); +    PMUL(1); +    PMUL(2); +    PMUL(3); +#undef PMUL + +    return d.ll; +} + +uint64_t helper_fmul8x16au(uint64_t src1, uint64_t src2) +{ +    VIS64 s, d; +    uint32_t tmp; + +    s.ll = src1; +    d.ll = src2; + +#define PMUL(r)                                                 \ +    tmp = (int32_t)d.VIS_SW64(0) * (int32_t)s.VIS_B64(r);       \ +    if ((tmp & 0xff) > 0x7f) {                                  \ +        tmp += 0x100;                                           \ +    }                                                           \ +    d.VIS_W64(r) = tmp >> 8; + +    PMUL(0); +    PMUL(1); +    PMUL(2); +    PMUL(3); +#undef PMUL + +    return d.ll; +} + +uint64_t helper_fmul8sux16(uint64_t src1, uint64_t src2) +{ +    VIS64 s, d; +    uint32_t tmp; + +    s.ll = src1; +    d.ll = src2; + +#define PMUL(r)                                                         \ +    tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8);       \ +    if ((tmp & 0xff) > 0x7f) {                                          \ +        tmp += 0x100;                                                   \ +    }                                                                   \ +    d.VIS_W64(r) = tmp >> 8; + +    PMUL(0); +    PMUL(1); +    PMUL(2); +    PMUL(3); +#undef PMUL + +    return d.ll; +} + +uint64_t helper_fmul8ulx16(uint64_t src1, uint64_t src2) +{ +    VIS64 s, d; +    uint32_t tmp; + +    s.ll = src1; +    d.ll = src2; + +#define PMUL(r)                                                         \ +    tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2));        \ +    if ((tmp & 0xff) > 0x7f) {                                          \ +        tmp += 0x100;                                                   \ +    }                                                                   \ +    d.VIS_W64(r) = tmp >> 8; + +    PMUL(0); +    PMUL(1); +    PMUL(2); +    PMUL(3); +#undef PMUL + +    return d.ll; +} + +uint64_t helper_fmuld8sux16(uint64_t src1, uint64_t src2) +{ +    VIS64 s, d; +    uint32_t tmp; + +    s.ll = src1; +    d.ll = src2; + +#define PMUL(r)                                                         \ +    tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8);       \ +    if ((tmp & 0xff) > 0x7f) {                                          \ +        tmp += 0x100;                                                   \ +    }                                                                   \ +    d.VIS_L64(r) = tmp; + +    /* Reverse calculation order to handle overlap */ +    PMUL(1); +    PMUL(0); +#undef PMUL + +    return d.ll; +} + +uint64_t helper_fmuld8ulx16(uint64_t src1, uint64_t src2) +{ +    VIS64 s, d; +    uint32_t tmp; + +    s.ll = src1; +    d.ll = src2; + +#define PMUL(r)                                                         \ +    tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2));        \ +    if ((tmp & 0xff) > 0x7f) {                                          \ +        tmp += 0x100;                                                   \ +    }                                                                   \ +    d.VIS_L64(r) = tmp; + +    /* Reverse calculation order to handle overlap */ +    PMUL(1); +    PMUL(0); +#undef PMUL + +    return d.ll; +} + +uint64_t helper_fexpand(uint64_t src1, uint64_t src2) +{ +    VIS32 s; +    VIS64 d; + +    s.l = (uint32_t)src1; +    d.ll = src2; +    d.VIS_W64(0) = s.VIS_B32(0) << 4; +    d.VIS_W64(1) = s.VIS_B32(1) << 4; +    d.VIS_W64(2) = s.VIS_B32(2) << 4; +    d.VIS_W64(3) = s.VIS_B32(3) << 4; + +    return d.ll; +} + +#define VIS_HELPER(name, F)                             \ +    uint64_t name##16(uint64_t src1, uint64_t src2)     \ +    {                                                   \ +        VIS64 s, d;                                     \ +                                                        \ +        s.ll = src1;                                    \ +        d.ll = src2;                                    \ +                                                        \ +        d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0));   \ +        d.VIS_W64(1) = F(d.VIS_W64(1), s.VIS_W64(1));   \ +        d.VIS_W64(2) = F(d.VIS_W64(2), s.VIS_W64(2));   \ +        d.VIS_W64(3) = F(d.VIS_W64(3), s.VIS_W64(3));   \ +                                                        \ +        return d.ll;                                    \ +    }                                                   \ +                                                        \ +    uint32_t name##16s(uint32_t src1, uint32_t src2)    \ +    {                                                   \ +        VIS32 s, d;                                     \ +                                                        \ +        s.l = src1;                                     \ +        d.l = src2;                                     \ +                                                        \ +        d.VIS_W32(0) = F(d.VIS_W32(0), s.VIS_W32(0));   \ +        d.VIS_W32(1) = F(d.VIS_W32(1), s.VIS_W32(1));   \ +                                                        \ +        return d.l;                                     \ +    }                                                   \ +                                                        \ +    uint64_t name##32(uint64_t src1, uint64_t src2)     \ +    {                                                   \ +        VIS64 s, d;                                     \ +                                                        \ +        s.ll = src1;                                    \ +        d.ll = src2;                                    \ +                                                        \ +        d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0));   \ +        d.VIS_L64(1) = F(d.VIS_L64(1), s.VIS_L64(1));   \ +                                                        \ +        return d.ll;                                    \ +    }                                                   \ +                                                        \ +    uint32_t name##32s(uint32_t src1, uint32_t src2)    \ +    {                                                   \ +        VIS32 s, d;                                     \ +                                                        \ +        s.l = src1;                                     \ +        d.l = src2;                                     \ +                                                        \ +        d.l = F(d.l, s.l);                              \ +                                                        \ +        return d.l;                                     \ +    } + +#define FADD(a, b) ((a) + (b)) +#define FSUB(a, b) ((a) - (b)) +VIS_HELPER(helper_fpadd, FADD) +VIS_HELPER(helper_fpsub, FSUB) + +#define VIS_CMPHELPER(name, F)                                    \ +    uint64_t name##16(uint64_t src1, uint64_t src2)               \ +    {                                                             \ +        VIS64 s, d;                                               \ +                                                                  \ +        s.ll = src1;                                              \ +        d.ll = src2;                                              \ +                                                                  \ +        d.VIS_W64(0) = F(s.VIS_W64(0), d.VIS_W64(0)) ? 1 : 0;     \ +        d.VIS_W64(0) |= F(s.VIS_W64(1), d.VIS_W64(1)) ? 2 : 0;    \ +        d.VIS_W64(0) |= F(s.VIS_W64(2), d.VIS_W64(2)) ? 4 : 0;    \ +        d.VIS_W64(0) |= F(s.VIS_W64(3), d.VIS_W64(3)) ? 8 : 0;    \ +        d.VIS_W64(1) = d.VIS_W64(2) = d.VIS_W64(3) = 0;           \ +                                                                  \ +        return d.ll;                                              \ +    }                                                             \ +                                                                  \ +    uint64_t name##32(uint64_t src1, uint64_t src2)               \ +    {                                                             \ +        VIS64 s, d;                                               \ +                                                                  \ +        s.ll = src1;                                              \ +        d.ll = src2;                                              \ +                                                                  \ +        d.VIS_L64(0) = F(s.VIS_L64(0), d.VIS_L64(0)) ? 1 : 0;     \ +        d.VIS_L64(0) |= F(s.VIS_L64(1), d.VIS_L64(1)) ? 2 : 0;    \ +        d.VIS_L64(1) = 0;                                         \ +                                                                  \ +        return d.ll;                                              \ +    } + +#define FCMPGT(a, b) ((a) > (b)) +#define FCMPEQ(a, b) ((a) == (b)) +#define FCMPLE(a, b) ((a) <= (b)) +#define FCMPNE(a, b) ((a) != (b)) + +VIS_CMPHELPER(helper_fcmpgt, FCMPGT) +VIS_CMPHELPER(helper_fcmpeq, FCMPEQ) +VIS_CMPHELPER(helper_fcmple, FCMPLE) +VIS_CMPHELPER(helper_fcmpne, FCMPNE) + +uint64_t helper_pdist(uint64_t sum, uint64_t src1, uint64_t src2) +{ +    int i; +    for (i = 0; i < 8; i++) { +        int s1, s2; + +        s1 = (src1 >> (56 - (i * 8))) & 0xff; +        s2 = (src2 >> (56 - (i * 8))) & 0xff; + +        /* Absolute value of difference. */ +        s1 -= s2; +        if (s1 < 0) { +            s1 = -s1; +        } + +        sum += s1; +    } + +    return sum; +} + +uint32_t helper_fpack16(uint64_t gsr, uint64_t rs2) +{ +    int scale = (gsr >> 3) & 0xf; +    uint32_t ret = 0; +    int byte; + +    for (byte = 0; byte < 4; byte++) { +        uint32_t val; +        int16_t src = rs2 >> (byte * 16); +        int32_t scaled = src << scale; +        int32_t from_fixed = scaled >> 7; + +        val = (from_fixed < 0 ?  0 : +               from_fixed > 255 ?  255 : from_fixed); + +        ret |= val << (8 * byte); +    } + +    return ret; +} + +uint64_t helper_fpack32(uint64_t gsr, uint64_t rs1, uint64_t rs2) +{ +    int scale = (gsr >> 3) & 0x1f; +    uint64_t ret = 0; +    int word; + +    ret = (rs1 << 8) & ~(0x000000ff000000ffULL); +    for (word = 0; word < 2; word++) { +        uint64_t val; +        int32_t src = rs2 >> (word * 32); +        int64_t scaled = (int64_t)src << scale; +        int64_t from_fixed = scaled >> 23; + +        val = (from_fixed < 0 ? 0 : +               (from_fixed > 255) ? 255 : from_fixed); + +        ret |= val << (32 * word); +    } + +    return ret; +} + +uint32_t helper_fpackfix(uint64_t gsr, uint64_t rs2) +{ +    int scale = (gsr >> 3) & 0x1f; +    uint32_t ret = 0; +    int word; + +    for (word = 0; word < 2; word++) { +        uint32_t val; +        int32_t src = rs2 >> (word * 32); +        int64_t scaled = src << scale; +        int64_t from_fixed = scaled >> 16; + +        val = (from_fixed < -32768 ? -32768 : +               from_fixed > 32767 ?  32767 : from_fixed); + +        ret |= (val & 0xffff) << (word * 16); +    } + +    return ret; +} + +uint64_t helper_bshuffle(uint64_t gsr, uint64_t src1, uint64_t src2) +{ +    union { +        uint64_t ll[2]; +        uint8_t b[16]; +    } s; +    VIS64 r; +    uint32_t i, mask, host; + +    /* Set up S such that we can index across all of the bytes.  */ +#ifdef HOST_WORDS_BIGENDIAN +    s.ll[0] = src1; +    s.ll[1] = src2; +    host = 0; +#else +    s.ll[1] = src1; +    s.ll[0] = src2; +    host = 15; +#endif +    mask = gsr >> 32; + +    for (i = 0; i < 8; ++i) { +        unsigned e = (mask >> (28 - i*4)) & 0xf; +        r.VIS_B64(i) = s.b[e ^ host]; +    } + +    return r.ll; +} diff --git a/target-sparc/win_helper.c b/target-sparc/win_helper.c new file mode 100644 index 00000000..f01ae08f --- /dev/null +++ b/target-sparc/win_helper.c @@ -0,0 +1,393 @@ +/* + * Helpers for CWP and PSTATE handling + * + *  Copyright (c) 2003-2005 Fabrice Bellard + * + * 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 "trace.h" + +static inline void memcpy32(target_ulong *dst, const target_ulong *src) +{ +    dst[0] = src[0]; +    dst[1] = src[1]; +    dst[2] = src[2]; +    dst[3] = src[3]; +    dst[4] = src[4]; +    dst[5] = src[5]; +    dst[6] = src[6]; +    dst[7] = src[7]; +} + +void cpu_set_cwp(CPUSPARCState *env, int new_cwp) +{ +    /* put the modified wrap registers at their proper location */ +    if (env->cwp == env->nwindows - 1) { +        memcpy32(env->regbase, env->regbase + env->nwindows * 16); +    } +    env->cwp = new_cwp; + +    /* put the wrap registers at their temporary location */ +    if (new_cwp == env->nwindows - 1) { +        memcpy32(env->regbase + env->nwindows * 16, env->regbase); +    } +    env->regwptr = env->regbase + (new_cwp * 16); +} + +target_ulong cpu_get_psr(CPUSPARCState *env) +{ +    helper_compute_psr(env); + +#if !defined(TARGET_SPARC64) +    return env->version | (env->psr & PSR_ICC) | +        (env->psref ? PSR_EF : 0) | +        (env->psrpil << 8) | +        (env->psrs ? PSR_S : 0) | +        (env->psrps ? PSR_PS : 0) | +        (env->psret ? PSR_ET : 0) | env->cwp; +#else +    return env->psr & PSR_ICC; +#endif +} + +void cpu_put_psr(CPUSPARCState *env, target_ulong val) +{ +    env->psr = val & PSR_ICC; +#if !defined(TARGET_SPARC64) +    env->psref = (val & PSR_EF) ? 1 : 0; +    env->psrpil = (val & PSR_PIL) >> 8; +#endif +#if ((!defined(TARGET_SPARC64)) && !defined(CONFIG_USER_ONLY)) +    cpu_check_irqs(env); +#endif +#if !defined(TARGET_SPARC64) +    env->psrs = (val & PSR_S) ? 1 : 0; +    env->psrps = (val & PSR_PS) ? 1 : 0; +    env->psret = (val & PSR_ET) ? 1 : 0; +    cpu_set_cwp(env, val & PSR_CWP); +#endif +    env->cc_op = CC_OP_FLAGS; +} + +int cpu_cwp_inc(CPUSPARCState *env, int cwp) +{ +    if (unlikely(cwp >= env->nwindows)) { +        cwp -= env->nwindows; +    } +    return cwp; +} + +int cpu_cwp_dec(CPUSPARCState *env, int cwp) +{ +    if (unlikely(cwp < 0)) { +        cwp += env->nwindows; +    } +    return cwp; +} + +#ifndef TARGET_SPARC64 +void helper_rett(CPUSPARCState *env) +{ +    unsigned int cwp; + +    if (env->psret == 1) { +        helper_raise_exception(env, TT_ILL_INSN); +    } + +    env->psret = 1; +    cwp = cpu_cwp_inc(env, env->cwp + 1) ; +    if (env->wim & (1 << cwp)) { +        helper_raise_exception(env, TT_WIN_UNF); +    } +    cpu_set_cwp(env, cwp); +    env->psrs = env->psrps; +} + +/* XXX: use another pointer for %iN registers to avoid slow wrapping +   handling ? */ +void helper_save(CPUSPARCState *env) +{ +    uint32_t cwp; + +    cwp = cpu_cwp_dec(env, env->cwp - 1); +    if (env->wim & (1 << cwp)) { +        helper_raise_exception(env, TT_WIN_OVF); +    } +    cpu_set_cwp(env, cwp); +} + +void helper_restore(CPUSPARCState *env) +{ +    uint32_t cwp; + +    cwp = cpu_cwp_inc(env, env->cwp + 1); +    if (env->wim & (1 << cwp)) { +        helper_raise_exception(env, TT_WIN_UNF); +    } +    cpu_set_cwp(env, cwp); +} + +void helper_wrpsr(CPUSPARCState *env, target_ulong new_psr) +{ +    if ((new_psr & PSR_CWP) >= env->nwindows) { +        helper_raise_exception(env, TT_ILL_INSN); +    } else { +        cpu_put_psr(env, new_psr); +    } +} + +target_ulong helper_rdpsr(CPUSPARCState *env) +{ +    return cpu_get_psr(env); +} + +#else +/* XXX: use another pointer for %iN registers to avoid slow wrapping +   handling ? */ +void helper_save(CPUSPARCState *env) +{ +    uint32_t cwp; + +    cwp = cpu_cwp_dec(env, env->cwp - 1); +    if (env->cansave == 0) { +        helper_raise_exception(env, TT_SPILL | (env->otherwin != 0 ? +                                                (TT_WOTHER | +                                                 ((env->wstate & 0x38) >> 1)) : +                                                ((env->wstate & 0x7) << 2))); +    } else { +        if (env->cleanwin - env->canrestore == 0) { +            /* XXX Clean windows without trap */ +            helper_raise_exception(env, TT_CLRWIN); +        } else { +            env->cansave--; +            env->canrestore++; +            cpu_set_cwp(env, cwp); +        } +    } +} + +void helper_restore(CPUSPARCState *env) +{ +    uint32_t cwp; + +    cwp = cpu_cwp_inc(env, env->cwp + 1); +    if (env->canrestore == 0) { +        helper_raise_exception(env, TT_FILL | (env->otherwin != 0 ? +                                               (TT_WOTHER | +                                                ((env->wstate & 0x38) >> 1)) : +                                               ((env->wstate & 0x7) << 2))); +    } else { +        env->cansave++; +        env->canrestore--; +        cpu_set_cwp(env, cwp); +    } +} + +void helper_flushw(CPUSPARCState *env) +{ +    if (env->cansave != env->nwindows - 2) { +        helper_raise_exception(env, TT_SPILL | (env->otherwin != 0 ? +                                                (TT_WOTHER | +                                                 ((env->wstate & 0x38) >> 1)) : +                                                ((env->wstate & 0x7) << 2))); +    } +} + +void helper_saved(CPUSPARCState *env) +{ +    env->cansave++; +    if (env->otherwin == 0) { +        env->canrestore--; +    } else { +        env->otherwin--; +    } +} + +void helper_restored(CPUSPARCState *env) +{ +    env->canrestore++; +    if (env->cleanwin < env->nwindows - 1) { +        env->cleanwin++; +    } +    if (env->otherwin == 0) { +        env->cansave--; +    } else { +        env->otherwin--; +    } +} + +target_ulong cpu_get_ccr(CPUSPARCState *env) +{ +    target_ulong psr; + +    psr = cpu_get_psr(env); + +    return ((env->xcc >> 20) << 4) | ((psr & PSR_ICC) >> 20); +} + +void cpu_put_ccr(CPUSPARCState *env, target_ulong val) +{ +    env->xcc = (val >> 4) << 20; +    env->psr = (val & 0xf) << 20; +    CC_OP = CC_OP_FLAGS; +} + +target_ulong cpu_get_cwp64(CPUSPARCState *env) +{ +    return env->nwindows - 1 - env->cwp; +} + +void cpu_put_cwp64(CPUSPARCState *env, int cwp) +{ +    if (unlikely(cwp >= env->nwindows || cwp < 0)) { +        cwp %= env->nwindows; +    } +    cpu_set_cwp(env, env->nwindows - 1 - cwp); +} + +target_ulong helper_rdccr(CPUSPARCState *env) +{ +    return cpu_get_ccr(env); +} + +void helper_wrccr(CPUSPARCState *env, target_ulong new_ccr) +{ +    cpu_put_ccr(env, new_ccr); +} + +/* CWP handling is reversed in V9, but we still use the V8 register +   order. */ +target_ulong helper_rdcwp(CPUSPARCState *env) +{ +    return cpu_get_cwp64(env); +} + +void helper_wrcwp(CPUSPARCState *env, target_ulong new_cwp) +{ +    cpu_put_cwp64(env, new_cwp); +} + +static inline uint64_t *get_gregset(CPUSPARCState *env, uint32_t pstate) +{ +    switch (pstate) { +    default: +        trace_win_helper_gregset_error(pstate); +        /* pass through to normal set of global registers */ +    case 0: +        return env->bgregs; +    case PS_AG: +        return env->agregs; +    case PS_MG: +        return env->mgregs; +    case PS_IG: +        return env->igregs; +    } +} + +void cpu_change_pstate(CPUSPARCState *env, uint32_t new_pstate) +{ +    uint32_t pstate_regs, new_pstate_regs; +    uint64_t *src, *dst; + +    if (env->def->features & CPU_FEATURE_GL) { +        /* PS_AG is not implemented in this case */ +        new_pstate &= ~PS_AG; +    } + +    pstate_regs = env->pstate & 0xc01; +    new_pstate_regs = new_pstate & 0xc01; + +    if (new_pstate_regs != pstate_regs) { +        trace_win_helper_switch_pstate(pstate_regs, new_pstate_regs); + +        /* Switch global register bank */ +        src = get_gregset(env, new_pstate_regs); +        dst = get_gregset(env, pstate_regs); +        memcpy32(dst, env->gregs); +        memcpy32(env->gregs, src); +    } else { +        trace_win_helper_no_switch_pstate(new_pstate_regs); +    } +    env->pstate = new_pstate; +} + +void helper_wrpstate(CPUSPARCState *env, target_ulong new_state) +{ +    cpu_change_pstate(env, new_state & 0xf3f); + +#if !defined(CONFIG_USER_ONLY) +    if (cpu_interrupts_enabled(env)) { +        cpu_check_irqs(env); +    } +#endif +} + +void helper_wrpil(CPUSPARCState *env, target_ulong new_pil) +{ +#if !defined(CONFIG_USER_ONLY) +    trace_win_helper_wrpil(env->psrpil, (uint32_t)new_pil); + +    env->psrpil = new_pil; + +    if (cpu_interrupts_enabled(env)) { +        cpu_check_irqs(env); +    } +#endif +} + +void helper_done(CPUSPARCState *env) +{ +    trap_state *tsptr = cpu_tsptr(env); + +    env->pc = tsptr->tnpc; +    env->npc = tsptr->tnpc + 4; +    cpu_put_ccr(env, tsptr->tstate >> 32); +    env->asi = (tsptr->tstate >> 24) & 0xff; +    cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f); +    cpu_put_cwp64(env, tsptr->tstate & 0xff); +    env->tl--; + +    trace_win_helper_done(env->tl); + +#if !defined(CONFIG_USER_ONLY) +    if (cpu_interrupts_enabled(env)) { +        cpu_check_irqs(env); +    } +#endif +} + +void helper_retry(CPUSPARCState *env) +{ +    trap_state *tsptr = cpu_tsptr(env); + +    env->pc = tsptr->tpc; +    env->npc = tsptr->tnpc; +    cpu_put_ccr(env, tsptr->tstate >> 32); +    env->asi = (tsptr->tstate >> 24) & 0xff; +    cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f); +    cpu_put_cwp64(env, tsptr->tstate & 0xff); +    env->tl--; + +    trace_win_helper_retry(env->tl); + +#if !defined(CONFIG_USER_ONLY) +    if (cpu_interrupts_enabled(env)) { +        cpu_check_irqs(env); +    } +#endif +} +#endif  | 
