blob: e7691e44587d38e42c84cc5eef60314353cb5bf9 [file] [log] [blame]
/*
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 "qemu/osdep.h"
#include "cpu.h"
#include "disas/disas.h"
#include "exec/helper-proto.h"
#include "exec/exec-all.h"
#include "tcg-op.h"
#include "exec/cpu_ldst.h"
#include "exec/helper-gen.h"
#include "trace-tcg.h"
#include "exec/log.h"
#include "asi.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_env cpu_env;
static TCGv_ptr 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;
static TCGv cpu_regs[32];
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_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;
#else
static TCGv cpu_wim;
#endif
/* Floating point registers */
static TCGv_i64 cpu_fpr[TARGET_DPREGS];
#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;
#ifdef TARGET_SPARC64
int fprs_dirty;
int asi;
#endif
} 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(DisasContext *dc, int rd)
{
#if defined(TARGET_SPARC64)
int bit = (rd < 32) ? 1 : 2;
/* If we know we've already set this bit within the TB,
we can avoid setting it again. */
if (!(dc->fprs_dirty & bit)) {
dc->fprs_dirty |= bit;
tcg_gen_ori_i32(cpu_fprs, cpu_fprs, bit);
}
#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_extrl_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(dc, 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(dc, 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(DisasContext *dc, 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(dc, 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) {
assert(reg < 32);
return cpu_regs[reg];
} else {
TCGv t = get_temp_tl(dc);
tcg_gen_movi_tl(t, 0);
return t;
}
}
static inline void gen_store_gpr(DisasContext *dc, int reg, TCGv v)
{
if (reg > 0) {
assert(reg < 32);
tcg_gen_mov_tl(cpu_regs[reg], v);
}
}
static inline TCGv gen_dest_gpr(DisasContext *dc, int reg)
{
if (reg > 0) {
assert(reg < 32);
return cpu_regs[reg];
} else {
return get_temp_tl(dc);
}
}
static inline bool use_goto_tb(DisasContext *s, target_ulong pc,
target_ulong npc)
{
if (unlikely(s->singlestep)) {
return false;
}
#ifndef CONFIG_USER_ONLY
return (pc & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK) &&
(npc & TARGET_PAGE_MASK) == (s->tb->pc & TARGET_PAGE_MASK);
#else
return true;
#endif
}
static inline void gen_goto_tb(DisasContext *s, int tb_num,
target_ulong pc, target_ulong npc)
{
if (use_goto_tb(s, pc, npc)) {
/* 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)s->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_extrl_i64_i32(cc_src1_32, cpu_cc_dst);
tcg_gen_extrl_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_extrl_i64_i32(cc_src1_32, cpu_cc_src);
tcg_gen_extrl_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 void gen_branch_a(DisasContext *dc, target_ulong pc1)
{
TCGLabel *l1 = gen_new_label();
target_ulong npc = dc->npc;
tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_cond, 0, l1);
gen_goto_tb(dc, 0, npc, pc1);
gen_set_label(l1);
gen_goto_tb(dc, 1, npc + 4, npc + 8);
dc->is_br = 1;
}
static void gen_branch_n(DisasContext *dc, target_ulong pc1)
{
target_ulong npc = dc->npc;
if (likely(npc != DYNAMIC_PC)) {
dc->pc = npc;
dc->jump_pc[0] = pc1;
dc->jump_pc[1] = npc + 4;
dc->npc = JUMP_PC;
} else {
TCGv t, z;
tcg_gen_mov_tl(cpu_pc, cpu_npc);
tcg_gen_addi_tl(cpu_npc, cpu_npc, 4);
t = tcg_const_tl(pc1);
z = tcg_const_tl(0);
tcg_gen_movcond_tl(TCG_COND_NE, cpu_npc, cpu_cond, z, t, cpu_npc);
tcg_temp_free(t);
tcg_temp_free(z);
dc->pc = DYNAMIC_PC;
}
}
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 void gen_exception(DisasContext *dc, int which)
{
TCGv_i32 t;
save_state(dc);
t = tcg_const_i32(which);
gen_helper_raise_exception(cpu_env, t);
tcg_temp_free_i32(t);
dc->is_br = 1;
}
static void gen_check_align(TCGv addr, int mask)
{
TCGv_i32 r_mask = tcg_const_i32(mask);
gen_helper_check_align(cpu_env, addr, r_mask);
tcg_temp_free_i32(r_mask);
}
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);
} else {
gen_branch_n(dc, target);
}
}
}
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);
} else {
gen_branch_n(dc, target);
}
}
}
#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);
} else {
gen_branch_n(dc, target);
}
}
static inline void gen_op_fcmps(int fccno, TCGv_i32 r_rs1, TCGv_i32 r_rs2)
{
switch (fccno) {
case 0:
gen_helper_fcmps(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
case 1:
gen_helper_fcmps_fcc1(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
case 2:
gen_helper_fcmps_fcc2(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
case 3:
gen_helper_fcmps_fcc3(cpu_fsr, 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_fsr, cpu_env, r_rs1, r_rs2);
break;
case 1:
gen_helper_fcmpd_fcc1(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
case 2:
gen_helper_fcmpd_fcc2(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
case 3:
gen_helper_fcmpd_fcc3(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
}
}
static inline void gen_op_fcmpq(int fccno)
{
switch (fccno) {
case 0:
gen_helper_fcmpq(cpu_fsr, cpu_env);
break;
case 1:
gen_helper_fcmpq_fcc1(cpu_fsr, cpu_env);
break;
case 2:
gen_helper_fcmpq_fcc2(cpu_fsr, cpu_env);
break;
case 3:
gen_helper_fcmpq_fcc3(cpu_fsr, 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_fsr, cpu_env, r_rs1, r_rs2);
break;
case 1:
gen_helper_fcmpes_fcc1(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
case 2:
gen_helper_fcmpes_fcc2(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
case 3:
gen_helper_fcmpes_fcc3(cpu_fsr, 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_fsr, cpu_env, r_rs1, r_rs2);
break;
case 1:
gen_helper_fcmped_fcc1(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
case 2:
gen_helper_fcmped_fcc2(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
case 3:
gen_helper_fcmped_fcc3(cpu_fsr, cpu_env, r_rs1, r_rs2);
break;
}
}
static inline void gen_op_fcmpeq(int fccno)
{
switch (fccno) {
case 0:
gen_helper_fcmpeq(cpu_fsr, cpu_env);
break;
case 1:
gen_helper_fcmpeq_fcc1(cpu_fsr, cpu_env);
break;
case 2:
gen_helper_fcmpeq_fcc2(cpu_fsr, cpu_env);
break;
case 3:
gen_helper_fcmpeq_fcc3(cpu_fsr, cpu_env);
break;
}
}
#else
static inline void gen_op_fcmps(int fccno, TCGv r_rs1, TCGv r_rs2)
{
gen_helper_fcmps(cpu_fsr, 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_fsr, cpu_env, r_rs1, r_rs2);
}
static inline void gen_op_fcmpq(int fccno)
{
gen_helper_fcmpq(cpu_fsr, cpu_env);
}
static inline void gen_op_fcmpes(int fccno, TCGv r_rs1, TCGv r_rs2)
{
gen_helper_fcmpes(cpu_fsr, 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_fsr, cpu_env, r_rs1, r_rs2);
}
static inline void gen_op_fcmpeq(int fccno)
{
gen_helper_fcmpeq(cpu_fsr, cpu_env);
}
#endif
static void gen_op_fpexception_im(DisasContext *dc, int fsr_flags)
{
tcg_gen_andi_tl(cpu_fsr, cpu_fsr, FSR_FTT_NMASK);
tcg_gen_ori_tl(cpu_fsr, cpu_fsr, fsr_flags);
gen_exception(dc, TT_FP_EXCP);
}
static int gen_trap_ifnofpu(DisasContext *dc)
{
#if !defined(CONFIG_USER_ONLY)
if (!dc->fpu_enabled) {
gen_exception(dc, TT_NFPU_INSN);
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_helper_check_ieee_exceptions(cpu_fsr, cpu_env);
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_helper_check_ieee_exceptions(cpu_fsr, cpu_env);
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_helper_check_ieee_exceptions(cpu_fsr, cpu_env);
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_helper_check_ieee_exceptions(cpu_fsr, cpu_env);
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_helper_check_ieee_exceptions(cpu_fsr, cpu_env);
gen_op_store_QT0_fpr(QFPREG(rd));
gen_update_fprs_dirty(dc, 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(dc, 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_helper_check_ieee_exceptions(cpu_fsr, cpu_env);
gen_op_store_QT0_fpr(QFPREG(rd));
gen_update_fprs_dirty(dc, 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_helper_check_ieee_exceptions(cpu_fsr, cpu_env);
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_helper_check_ieee_exceptions(cpu_fsr, cpu_env);
gen_op_store_QT0_fpr(QFPREG(rd));
gen_update_fprs_dirty(dc, 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_helper_check_ieee_exceptions(cpu_fsr, cpu_env);
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_helper_check_ieee_exceptions(cpu_fsr, cpu_env);
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_helper_check_ieee_exceptions(cpu_fsr, 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_helper_check_ieee_exceptions(cpu_fsr, 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(dc, 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(dc, QFPREG(rd));
}
/* asi moves */
#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
typedef enum {
GET_ASI_HELPER,
GET_ASI_EXCP,
GET_ASI_DIRECT,
GET_ASI_DTWINX,
GET_ASI_BLOCK,
GET_ASI_SHORT,
} ASIType;
typedef struct {
ASIType type;
int asi;
int mem_idx;
TCGMemOp memop;
} DisasASI;
static DisasASI get_asi(DisasContext *dc, int insn, TCGMemOp memop)
{
int asi = GET_FIELD(insn, 19, 26);
ASIType type = GET_ASI_HELPER;
int mem_idx = dc->mem_idx;
#ifndef TARGET_SPARC64
/* Before v9, all asis are immediate and privileged. */
if (IS_IMM) {
gen_exception(dc, TT_ILL_INSN);
type = GET_ASI_EXCP;
} else if (supervisor(dc)
/* Note that LEON accepts ASI_USERDATA in user mode, for
use with CASA. Also note that previous versions of
QEMU allowed (and old versions of gcc emitted) ASI_P
for LEON, which is incorrect. */
|| (asi == ASI_USERDATA
&& (dc->def->features & CPU_FEATURE_CASA))) {
switch (asi) {
case ASI_USERDATA: /* User data access */
mem_idx = MMU_USER_IDX;
type = GET_ASI_DIRECT;
break;
case ASI_KERNELDATA: /* Supervisor data access */
mem_idx = MMU_KERNEL_IDX;
type = GET_ASI_DIRECT;
break;
}
} else {
gen_exception(dc, TT_PRIV_INSN);
type = GET_ASI_EXCP;
}
#else
if (IS_IMM) {
asi = dc->asi;
}
/* With v9, all asis below 0x80 are privileged. */
/* ??? We ought to check cpu_has_hypervisor, but we didn't copy
down that bit into DisasContext. For the moment that's ok,
since the direct implementations below doesn't have any ASIs
in the restricted [0x30, 0x7f] range, and the check will be
done properly in the helper. */
if (!supervisor(dc) && asi < 0x80) {
gen_exception(dc, TT_PRIV_ACT);
type = GET_ASI_EXCP;
} else {
switch (asi) {
case ASI_N: /* Nucleus */
case ASI_NL: /* Nucleus LE */
case ASI_TWINX_N:
case ASI_TWINX_NL:
mem_idx = MMU_NUCLEUS_IDX;
break;
case ASI_AIUP: /* As if user primary */
case ASI_AIUPL: /* As if user primary LE */
case ASI_TWINX_AIUP:
case ASI_TWINX_AIUP_L:
case ASI_BLK_AIUP_4V:
case ASI_BLK_AIUP_L_4V:
case ASI_BLK_AIUP:
case ASI_BLK_AIUPL:
mem_idx = MMU_USER_IDX;
break;
case ASI_AIUS: /* As if user secondary */
case ASI_AIUSL: /* As if user secondary LE */
case ASI_TWINX_AIUS:
case ASI_TWINX_AIUS_L:
case ASI_BLK_AIUS_4V:
case ASI_BLK_AIUS_L_4V:
case ASI_BLK_AIUS:
case ASI_BLK_AIUSL:
mem_idx = MMU_USER_SECONDARY_IDX;
break;
case ASI_S: /* Secondary */
case ASI_SL: /* Secondary LE */
case ASI_TWINX_S:
case ASI_TWINX_SL:
case ASI_BLK_COMMIT_S:
case ASI_BLK_S:
case ASI_BLK_SL:
case ASI_FL8_S:
case ASI_FL8_SL:
case ASI_FL16_S:
case ASI_FL16_SL:
if (mem_idx == MMU_USER_IDX) {
mem_idx = MMU_USER_SECONDARY_IDX;
} else if (mem_idx == MMU_KERNEL_IDX) {
mem_idx = MMU_KERNEL_SECONDARY_IDX;
}
break;
case ASI_P: /* Primary */
case ASI_PL: /* Primary LE */
case ASI_TWINX_P:
case ASI_TWINX_PL:
case ASI_BLK_COMMIT_P:
case ASI_BLK_P:
case ASI_BLK_PL:
case ASI_FL8_P:
case ASI_FL8_PL:
case ASI_FL16_P:
case ASI_FL16_PL:
break;
}
switch (asi) {
case ASI_N:
case ASI_NL:
case ASI_AIUP:
case ASI_AIUPL:
case ASI_AIUS:
case ASI_AIUSL:
case ASI_S:
case ASI_SL:
case ASI_P:
case ASI_PL:
type = GET_ASI_DIRECT;
break;
case ASI_TWINX_N:
case ASI_TWINX_NL:
case ASI_TWINX_AIUP:
case ASI_TWINX_AIUP_L:
case ASI_TWINX_AIUS:
case ASI_TWINX_AIUS_L:
case ASI_TWINX_P:
case ASI_TWINX_PL:
case ASI_TWINX_S:
case ASI_TWINX_SL:
type = GET_ASI_DTWINX;
break;
case ASI_BLK_COMMIT_P:
case ASI_BLK_COMMIT_S:
case ASI_BLK_AIUP_4V:
case ASI_BLK_AIUP_L_4V:
case ASI_BLK_AIUP:
case ASI_BLK_AIUPL:
case ASI_BLK_AIUS_4V:
case ASI_BLK_AIUS_L_4V:
case ASI_BLK_AIUS:
case ASI_BLK_AIUSL:
case ASI_BLK_S:
case ASI_BLK_SL:
case ASI_BLK_P:
case ASI_BLK_PL:
type = GET_ASI_BLOCK;
break;
case ASI_FL8_S:
case ASI_FL8_SL:
case ASI_FL8_P:
case ASI_FL8_PL:
memop = MO_UB;
type = GET_ASI_SHORT;
break;
case ASI_FL16_S:
case ASI_FL16_SL:
case ASI_FL16_P:
case ASI_FL16_PL:
memop = MO_TEUW;
type = GET_ASI_SHORT;
break;
}
/* The little-endian asis all have bit 3 set. */
if (asi & 8) {
memop ^= MO_BSWAP;
}
}
#endif
return (DisasASI){ type, asi, mem_idx, memop };
}
static void gen_ld_asi(DisasContext *dc, TCGv dst, TCGv addr,
int insn, TCGMemOp memop)
{
DisasASI da = get_asi(dc, insn, memop);
switch (da.type) {
case GET_ASI_EXCP:
break;
case GET_ASI_DTWINX: /* Reserved for ldda. */
gen_exception(dc, TT_ILL_INSN);
break;
case GET_ASI_DIRECT:
gen_address_mask(dc, addr);
tcg_gen_qemu_ld_tl(dst, addr, da.mem_idx, da.memop);
break;
default:
{
TCGv_i32 r_asi = tcg_const_i32(da.asi);
TCGv_i32 r_mop = tcg_const_i32(memop);
save_state(dc);
#ifdef TARGET_SPARC64
gen_helper_ld_asi(dst, cpu_env, addr, r_asi, r_mop);
#else
{
TCGv_i64 t64 = tcg_temp_new_i64();
gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_mop);
tcg_gen_trunc_i64_tl(dst, t64);
tcg_temp_free_i64(t64);
}
#endif
tcg_temp_free_i32(r_mop);
tcg_temp_free_i32(r_asi);
}
break;
}
}
static void gen_st_asi(DisasContext *dc, TCGv src, TCGv addr,
int insn, TCGMemOp memop)
{
DisasASI da = get_asi(dc, insn, memop);
switch (da.type) {
case GET_ASI_EXCP:
break;
case GET_ASI_DTWINX: /* Reserved for stda. */
gen_exception(dc, TT_ILL_INSN);
break;
case GET_ASI_DIRECT:
gen_address_mask(dc, addr);
tcg_gen_qemu_st_tl(src, addr, da.mem_idx, da.memop);
break;
default:
{
TCGv_i32 r_asi = tcg_const_i32(da.asi);
TCGv_i32 r_mop = tcg_const_i32(memop & MO_SIZE);
save_state(dc);
#ifdef TARGET_SPARC64
gen_helper_st_asi(cpu_env, addr, src, r_asi, r_mop);
#else
{
TCGv_i64 t64 = tcg_temp_new_i64();
tcg_gen_extu_tl_i64(t64, src);
gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_mop);
tcg_temp_free_i64(t64);
}
#endif
tcg_temp_free_i32(r_mop);
tcg_temp_free_i32(r_asi);
/* A write to a TLB register may alter page maps. End the TB. */
dc->npc = DYNAMIC_PC;
}
break;
}
}
static void gen_swap_asi(DisasContext *dc, TCGv dst, TCGv src,
TCGv addr, int insn)
{
DisasASI da = get_asi(dc, insn, MO_TEUL);
switch (da.type) {
case GET_ASI_EXCP:
break;
default:
{
TCGv_i32 r_asi = tcg_const_i32(da.asi);
TCGv_i32 r_mop = tcg_const_i32(MO_UL);
TCGv_i64 s64, t64;
save_state(dc);
t64 = tcg_temp_new_i64();
gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_mop);
s64 = tcg_temp_new_i64();
tcg_gen_extu_tl_i64(s64, src);
gen_helper_st_asi(cpu_env, addr, s64, r_asi, r_mop);
tcg_temp_free_i64(s64);
tcg_temp_free_i32(r_mop);
tcg_temp_free_i32(r_asi);
tcg_gen_trunc_i64_tl(dst, t64);
tcg_temp_free_i64(t64);
}
break;
}
}
static void gen_cas_asi(DisasContext *dc, TCGv addr, TCGv val2,
int insn, int rd)
{
DisasASI da = get_asi(dc, insn, MO_TEUL);
TCGv val1, dst;
TCGv_i32 r_asi;
if (da.type == GET_ASI_EXCP) {
return;
}
save_state(dc);
val1 = gen_load_gpr(dc, rd);
dst = gen_dest_gpr(dc, rd);
r_asi = tcg_const_i32(da.asi);
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 void gen_ldstub_asi(DisasContext *dc, TCGv dst, TCGv addr, int insn)
{
DisasASI da = get_asi(dc, insn, MO_UB);
switch (da.type) {
case GET_ASI_EXCP:
break;
default:
{
TCGv_i32 r_asi = tcg_const_i32(da.asi);
TCGv_i32 r_mop = tcg_const_i32(MO_UB);
TCGv_i64 s64, t64;
save_state(dc);
t64 = tcg_temp_new_i64();
gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_mop);
s64 = tcg_const_i64(0xff);
gen_helper_st_asi(cpu_env, addr, s64, r_asi, r_mop);
tcg_temp_free_i64(s64);
tcg_temp_free_i32(r_mop);
tcg_temp_free_i32(r_asi);
tcg_gen_trunc_i64_tl(dst, t64);
tcg_temp_free_i64(t64);
}
break;
}
}
#endif
#ifdef TARGET_SPARC64
static void gen_ldf_asi(DisasContext *dc, TCGv addr,
int insn, int size, int rd)
{
DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEQ));
TCGv_i32 d32;
switch (da.type) {
case GET_ASI_EXCP:
break;
case GET_ASI_DIRECT:
gen_address_mask(dc, addr);
switch (size) {
case 4:
d32 = gen_dest_fpr_F(dc);
tcg_gen_qemu_ld_i32(d32, addr, da.mem_idx, da.memop);
gen_store_fpr_F(dc, rd, d32);
break;
case 8:
tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop);
break;
case 16:
tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop);
tcg_gen_addi_tl(addr, addr, 8);
tcg_gen_qemu_ld_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, da.memop);
break;
default:
g_assert_not_reached();
}
break;
case GET_ASI_BLOCK:
/* Valid for lddfa on aligned registers only. */
if (size == 8 && (rd & 7) == 0) {
TCGv eight;
int i;
gen_check_align(addr, 0x3f);
gen_address_mask(dc, addr);
eight = tcg_const_tl(8);
for (i = 0; ; ++i) {
tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2 + i], addr,
da.mem_idx, da.memop);
if (i == 7) {
break;
}
tcg_gen_add_tl(addr, addr, eight);
}
tcg_temp_free(eight);
} else {
gen_exception(dc, TT_ILL_INSN);
}
break;
case GET_ASI_SHORT:
/* Valid for lddfa only. */
if (size == 8) {
gen_address_mask(dc, addr);
tcg_gen_qemu_ld_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop);
} else {
gen_exception(dc, TT_ILL_INSN);
}
break;
default:
{
TCGv_i32 r_asi = tcg_const_i32(da.asi);
TCGv_i32 r_mop = tcg_const_i32(da.memop);
save_state(dc);
/* According to the table in the UA2011 manual, the only
other asis that are valid for ldfa/lddfa/ldqfa are
the NO_FAULT asis. We still need a helper for these,
but we can just use the integer asi helper for them. */
switch (size) {
case 4:
{
TCGv d64 = tcg_temp_new_i64();
gen_helper_ld_asi(d64, cpu_env, addr, r_asi, r_mop);
d32 = gen_dest_fpr_F(dc);
tcg_gen_extrl_i64_i32(d32, d64);
tcg_temp_free_i64(d64);
gen_store_fpr_F(dc, rd, d32);
}
break;
case 8:
gen_helper_ld_asi(cpu_fpr[rd / 2], cpu_env, addr, r_asi, r_mop);
break;
case 16:
gen_helper_ld_asi(cpu_fpr[rd / 2], cpu_env, addr, r_asi, r_mop);
tcg_gen_addi_tl(addr, addr, 8);
gen_helper_ld_asi(cpu_fpr[rd/2+1], cpu_env, addr, r_asi, r_mop);
break;
default:
g_assert_not_reached();
}
tcg_temp_free_i32(r_mop);
tcg_temp_free_i32(r_asi);
}
break;
}
}
static void gen_stf_asi(DisasContext *dc, TCGv addr,
int insn, int size, int rd)
{
DisasASI da = get_asi(dc, insn, (size == 4 ? MO_TEUL : MO_TEQ));
TCGv_i32 d32;
switch (da.type) {
case GET_ASI_EXCP:
break;
case GET_ASI_DIRECT:
gen_address_mask(dc, addr);
switch (size) {
case 4:
d32 = gen_load_fpr_F(dc, rd);
tcg_gen_qemu_st_i32(d32, addr, da.mem_idx, da.memop);
break;
case 8:
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop);
break;
case 16:
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop);
tcg_gen_addi_tl(addr, addr, 8);
tcg_gen_qemu_st_i64(cpu_fpr[rd/2+1], addr, da.mem_idx, da.memop);
break;
default:
g_assert_not_reached();
}
break;
case GET_ASI_BLOCK:
/* Valid for stdfa on aligned registers only. */
if (size == 8 && (rd & 7) == 0) {
TCGv eight;
int i;
gen_check_align(addr, 0x3f);
gen_address_mask(dc, addr);
eight = tcg_const_tl(8);
for (i = 0; ; ++i) {
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2 + i], addr,
da.mem_idx, da.memop);
if (i == 7) {
break;
}
tcg_gen_add_tl(addr, addr, eight);
}
tcg_temp_free(eight);
} else {
gen_exception(dc, TT_ILL_INSN);
}
break;
case GET_ASI_SHORT:
/* Valid for stdfa only. */
if (size == 8) {
gen_address_mask(dc, addr);
tcg_gen_qemu_st_i64(cpu_fpr[rd / 2], addr, da.mem_idx, da.memop);
} else {
gen_exception(dc, TT_ILL_INSN);
}
break;
default:
/* According to the table in the UA2011 manual, the only
other asis that are valid for ldfa/lddfa/ldqfa are
the PST* asis, which aren't currently handled. */
gen_exception(dc, TT_ILL_INSN);
break;
}
}
static void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd)
{
DisasASI da = get_asi(dc, insn, MO_TEQ);
TCGv_i64 hi = gen_dest_gpr(dc, rd);
TCGv_i64 lo = gen_dest_gpr(dc, rd + 1);
switch (da.type) {
case GET_ASI_EXCP:
return;
case GET_ASI_DTWINX:
gen_check_align(addr, 15);
gen_address_mask(dc, addr);
tcg_gen_qemu_ld_i64(hi, addr, da.mem_idx, da.memop);
tcg_gen_addi_tl(addr, addr, 8);
tcg_gen_qemu_ld_i64(lo, addr, da.mem_idx, da.memop);
break;
case GET_ASI_DIRECT:
{
TCGv_i64 tmp = tcg_temp_new_i64();
gen_address_mask(dc, addr);
tcg_gen_qemu_ld_i64(tmp, addr, da.mem_idx, da.memop);
/* Note that LE ldda acts as if each 32-bit register
result is byte swapped. Having just performed one
64-bit bswap, we need now to swap the writebacks. */
if ((da.memop & MO_BSWAP) == MO_TE) {
tcg_gen_extr32_i64(lo, hi, tmp);
} else {
tcg_gen_extr32_i64(hi, lo, tmp);
}
tcg_temp_free_i64(tmp);
}
break;
default:
{
TCGv_i32 r_asi = tcg_const_i32(da.asi);
save_state(dc);
gen_helper_ldda_asi(cpu_env, addr, r_asi);
tcg_temp_free_i32(r_asi);
tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUSPARCState, qt0.high));
tcg_gen_ld_i64(lo, cpu_env, offsetof(CPUSPARCState, qt0.low));
}
break;
}
gen_store_gpr(dc, rd, hi);
gen_store_gpr(dc, rd + 1, lo);
}
static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr,
int insn, int rd)
{
DisasASI da = get_asi(dc, insn, MO_TEQ);
TCGv lo = gen_load_gpr(dc, rd + 1);
switch (da.type) {
case GET_ASI_EXCP:
break;
case GET_ASI_DTWINX:
gen_check_align(addr, 15);
gen_address_mask(dc, addr);
tcg_gen_qemu_st_i64(hi, addr, da.mem_idx, da.memop);
tcg_gen_addi_tl(addr, addr, 8);
tcg_gen_qemu_st_i64(lo, addr, da.mem_idx, da.memop);
break;
case GET_ASI_DIRECT:
{
TCGv_i64 t64 = tcg_temp_new_i64();
/* Note that LE stda acts as if each 32-bit register result is
byte swapped. We will perform one 64-bit LE store, so now
we must swap the order of the construction. */
if ((da.memop & MO_BSWAP) == MO_TE) {
tcg_gen_concat32_i64(t64, lo, hi);
} else {
tcg_gen_concat32_i64(t64, hi, lo);
}
gen_address_mask(dc, addr);
tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop);
tcg_temp_free_i64(t64);
}
break;
default:
{
TCGv_i32 r_asi = tcg_const_i32(da.asi);
TCGv_i32 r_mop = tcg_const_i32(MO_Q);
TCGv_i64 t64;
save_state(dc);
t64 = tcg_temp_new_i64();
tcg_gen_concat_tl_i64(t64, lo, hi);
gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_mop);
tcg_temp_free_i32(r_mop);
tcg_temp_free_i32(r_asi);
tcg_temp_free_i64(t64);
}
break;
}
}
static void gen_casx_asi(DisasContext *dc, TCGv addr, TCGv val2,
int insn, int rd)
{
DisasASI da = get_asi(dc, insn, MO_TEQ);
TCGv val1 = gen_load_gpr(dc, rd);
TCGv dst = gen_dest_gpr(dc, rd);
TCGv_i32 r_asi;
if (da.type == GET_ASI_EXCP) {
return;
}
save_state(dc);
r_asi = tcg_const_i32(da.asi);
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 void gen_ldda_asi(DisasContext *dc, TCGv addr, int insn, int rd)
{
/* ??? Work around an apparent bug in Ubuntu gcc 4.8.2-10ubuntu2+12,
whereby "rd + 1" elicits "error: array subscript is above array".
Since we have already asserted that rd is even, the semantics
are unchanged. */
TCGv lo = gen_dest_gpr(dc, rd | 1);
TCGv hi = gen_dest_gpr(dc, rd);
TCGv_i64 t64 = tcg_temp_new_i64();
DisasASI da = get_asi(dc, insn, MO_TEQ);
switch (da.type) {
case GET_ASI_EXCP:
tcg_temp_free_i64(t64);
return;
case GET_ASI_DIRECT:
gen_address_mask(dc, addr);
tcg_gen_qemu_ld_i64(t64, addr, da.mem_idx, da.memop);
break;
default:
{
TCGv_i32 r_asi = tcg_const_i32(da.asi);
TCGv_i32 r_mop = tcg_const_i32(MO_Q);
save_state(dc);
gen_helper_ld_asi(t64, cpu_env, addr, r_asi, r_mop);
tcg_temp_free_i32(r_mop);
tcg_temp_free_i32(r_asi);
}
break;
}
tcg_gen_extr_i64_i32(lo, hi, t64);
tcg_temp_free_i64(t64);
gen_store_gpr(dc, rd | 1, lo);
gen_store_gpr(dc, rd, hi);
}
static void gen_stda_asi(DisasContext *dc, TCGv hi, TCGv addr,
int insn, int rd)
{
DisasASI da = get_asi(dc, insn, MO_TEQ);
TCGv lo = gen_load_gpr(dc, rd + 1);
TCGv_i64 t64 = tcg_temp_new_i64();
tcg_gen_concat_tl_i64(t64, lo, hi);
switch (da.type) {
case GET_ASI_EXCP:
break;
case GET_ASI_DIRECT:
gen_address_mask(dc, addr);
tcg_gen_qemu_st_i64(t64, addr, da.mem_idx, da.memop);
break;
default:
{
TCGv_i32 r_asi = tcg_const_i32(da.asi);
TCGv_i32 r_mop = tcg_const_i32(MO_Q);
save_state(dc);
gen_helper_st_asi(cpu_env, addr, t64, r_asi, r_mop);
tcg_temp_free_i32(r_mop);
tcg_temp_free_i32(r_asi);
}
break;
}
tcg_temp_free_i64(t64);
}
#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_extrl_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_extrl_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(dc, qd);
}
#ifndef CONFIG_USER_ONLY
static inline void gen_load_trap_state_at_tl(TCGv_ptr r_tsptr, TCGv_env 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;
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,