diff options
author | Guan Xuetao <gxt@mprc.pku.edu.cn> | 2011-04-12 16:25:59 +0800 |
---|---|---|
committer | Blue Swirl <blauwirbel@gmail.com> | 2011-04-12 18:48:37 +0000 |
commit | 6e64da3cd6890181fbeaaae7c08cfb779f138d82 (patch) | |
tree | 04730ffb2826bdc31046d45e24f4a7c893d06e4b /target-unicore32/translate.c | |
parent | 6d76d23e82d0ce8a7874b13f5951aa763e059908 (diff) |
unicore32: add target-unicore32 directory for unicore32-linux-user support
Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
Diffstat (limited to 'target-unicore32/translate.c')
-rw-r--r-- | target-unicore32/translate.c | 2105 |
1 files changed, 2105 insertions, 0 deletions
diff --git a/target-unicore32/translate.c b/target-unicore32/translate.c new file mode 100644 index 0000000000..a6ba991e92 --- /dev/null +++ b/target-unicore32/translate.c @@ -0,0 +1,2105 @@ +/* + * UniCore32 translation + * + * Copyright (C) 2010-2011 GUAN Xue-tao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> + +#include "cpu.h" +#include "exec-all.h" +#include "disas.h" +#include "tcg-op.h" +#include "qemu-log.h" + +#include "helper.h" +#define GEN_HELPER 1 +#include "helper.h" + +/* internal defines */ +typedef struct DisasContext { + target_ulong pc; + int is_jmp; + /* Nonzero if this instruction has been conditionally skipped. */ + int condjmp; + /* The label that will be jumped to when the instruction is skipped. */ + int condlabel; + struct TranslationBlock *tb; + int singlestep_enabled; +} DisasContext; + +#define IS_USER(s) 1 + +/* These instructions trap after executing, so defer them until after the + conditional executions state has been updated. */ +#define DISAS_SYSCALL 5 + +static TCGv_ptr cpu_env; +static TCGv_i32 cpu_R[32]; + +/* FIXME: These should be removed. */ +static TCGv cpu_F0s, cpu_F1s; +static TCGv_i64 cpu_F0d, cpu_F1d; + +#include "gen-icount.h" + +static const char *regnames[] = { + "r00", "r01", "r02", "r03", "r04", "r05", "r06", "r07", + "r08", "r09", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "pc" }; + +/* initialize TCG globals. */ +void uc32_translate_init(void) +{ + int i; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + + for (i = 0; i < 32; i++) { + cpu_R[i] = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUState, regs[i]), regnames[i]); + } + +#define GEN_HELPER 2 +#include "helper.h" +} + +static int num_temps; + +/* Allocate a temporary variable. */ +static TCGv_i32 new_tmp(void) +{ + num_temps++; + return tcg_temp_new_i32(); +} + +/* Release a temporary variable. */ +static void dead_tmp(TCGv tmp) +{ + tcg_temp_free(tmp); + num_temps--; +} + +static inline TCGv load_cpu_offset(int offset) +{ + TCGv tmp = new_tmp(); + tcg_gen_ld_i32(tmp, cpu_env, offset); + return tmp; +} + +#define load_cpu_field(name) load_cpu_offset(offsetof(CPUState, name)) + +static inline void store_cpu_offset(TCGv var, int offset) +{ + tcg_gen_st_i32(var, cpu_env, offset); + dead_tmp(var); +} + +#define store_cpu_field(var, name) \ + store_cpu_offset(var, offsetof(CPUState, name)) + +/* Set a variable to the value of a CPU register. */ +static void load_reg_var(DisasContext *s, TCGv var, int reg) +{ + if (reg == 31) { + uint32_t addr; + /* normaly, since we updated PC */ + addr = (long)s->pc; + tcg_gen_movi_i32(var, addr); + } else { + tcg_gen_mov_i32(var, cpu_R[reg]); + } +} + +/* Create a new temporary and set it to the value of a CPU register. */ +static inline TCGv load_reg(DisasContext *s, int reg) +{ + TCGv tmp = new_tmp(); + load_reg_var(s, tmp, reg); + return tmp; +} + +/* Set a CPU register. The source must be a temporary and will be + marked as dead. */ +static void store_reg(DisasContext *s, int reg, TCGv var) +{ + if (reg == 31) { + tcg_gen_andi_i32(var, var, ~3); + s->is_jmp = DISAS_JUMP; + } + tcg_gen_mov_i32(cpu_R[reg], var); + dead_tmp(var); +} + +/* Value extensions. */ +#define gen_uxtb(var) tcg_gen_ext8u_i32(var, var) +#define gen_uxth(var) tcg_gen_ext16u_i32(var, var) +#define gen_sxtb(var) tcg_gen_ext8s_i32(var, var) +#define gen_sxth(var) tcg_gen_ext16s_i32(var, var) + +#define UCOP_REG_M (((insn) >> 0) & 0x1f) +#define UCOP_REG_N (((insn) >> 19) & 0x1f) +#define UCOP_REG_D (((insn) >> 14) & 0x1f) +#define UCOP_REG_S (((insn) >> 9) & 0x1f) +#define UCOP_REG_LO (((insn) >> 14) & 0x1f) +#define UCOP_REG_HI (((insn) >> 9) & 0x1f) +#define UCOP_SH_OP (((insn) >> 6) & 0x03) +#define UCOP_SH_IM (((insn) >> 9) & 0x1f) +#define UCOP_OPCODES (((insn) >> 25) & 0x0f) +#define UCOP_IMM_9 (((insn) >> 0) & 0x1ff) +#define UCOP_IMM10 (((insn) >> 0) & 0x3ff) +#define UCOP_IMM14 (((insn) >> 0) & 0x3fff) +#define UCOP_COND (((insn) >> 25) & 0x0f) +#define UCOP_CMOV_COND (((insn) >> 19) & 0x0f) +#define UCOP_CPNUM (((insn) >> 10) & 0x0f) +#define UCOP_UCF64_FMT (((insn) >> 24) & 0x03) +#define UCOP_UCF64_FUNC (((insn) >> 6) & 0x0f) +#define UCOP_UCF64_COND (((insn) >> 6) & 0x0f) + +#define UCOP_SET(i) ((insn) & (1 << (i))) +#define UCOP_SET_P UCOP_SET(28) +#define UCOP_SET_U UCOP_SET(27) +#define UCOP_SET_B UCOP_SET(26) +#define UCOP_SET_W UCOP_SET(25) +#define UCOP_SET_L UCOP_SET(24) +#define UCOP_SET_S UCOP_SET(24) + +#define ILLEGAL cpu_abort(env, \ + "Illegal UniCore32 instruction %x at line %d!", \ + insn, __LINE__) + +static inline void gen_set_asr(TCGv var, uint32_t mask) +{ + TCGv tmp_mask = tcg_const_i32(mask); + gen_helper_asr_write(var, tmp_mask); + tcg_temp_free_i32(tmp_mask); +} +/* Set NZCV flags from the high 4 bits of var. */ +#define gen_set_nzcv(var) gen_set_asr(var, ASR_NZCV) + +static void gen_exception(int excp) +{ + TCGv tmp = new_tmp(); + tcg_gen_movi_i32(tmp, excp); + gen_helper_exception(tmp); + dead_tmp(tmp); +} + +/* FIXME: Most targets have native widening multiplication. + It would be good to use that instead of a full wide multiply. */ +/* 32x32->64 multiply. Marks inputs as dead. */ +static TCGv_i64 gen_mulu_i64_i32(TCGv a, TCGv b) +{ + TCGv_i64 tmp1 = tcg_temp_new_i64(); + TCGv_i64 tmp2 = tcg_temp_new_i64(); + + tcg_gen_extu_i32_i64(tmp1, a); + dead_tmp(a); + tcg_gen_extu_i32_i64(tmp2, b); + dead_tmp(b); + tcg_gen_mul_i64(tmp1, tmp1, tmp2); + tcg_temp_free_i64(tmp2); + return tmp1; +} + +static TCGv_i64 gen_muls_i64_i32(TCGv a, TCGv b) +{ + TCGv_i64 tmp1 = tcg_temp_new_i64(); + TCGv_i64 tmp2 = tcg_temp_new_i64(); + + tcg_gen_ext_i32_i64(tmp1, a); + dead_tmp(a); + tcg_gen_ext_i32_i64(tmp2, b); + dead_tmp(b); + tcg_gen_mul_i64(tmp1, tmp1, tmp2); + tcg_temp_free_i64(tmp2); + return tmp1; +} + +#define gen_set_CF(var) tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, CF)) + +/* Set CF to the top bit of var. */ +static void gen_set_CF_bit31(TCGv var) +{ + TCGv tmp = new_tmp(); + tcg_gen_shri_i32(tmp, var, 31); + gen_set_CF(tmp); + dead_tmp(tmp); +} + +/* Set N and Z flags from var. */ +static inline void gen_logic_CC(TCGv var) +{ + tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, NF)); + tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, ZF)); +} + +/* dest = T0 + T1 + CF. */ +static void gen_add_carry(TCGv dest, TCGv t0, TCGv t1) +{ + TCGv tmp; + tcg_gen_add_i32(dest, t0, t1); + tmp = load_cpu_field(CF); + tcg_gen_add_i32(dest, dest, tmp); + dead_tmp(tmp); +} + +/* dest = T0 - T1 + CF - 1. */ +static void gen_sub_carry(TCGv dest, TCGv t0, TCGv t1) +{ + TCGv tmp; + tcg_gen_sub_i32(dest, t0, t1); + tmp = load_cpu_field(CF); + tcg_gen_add_i32(dest, dest, tmp); + tcg_gen_subi_i32(dest, dest, 1); + dead_tmp(tmp); +} + +static void shifter_out_im(TCGv var, int shift) +{ + TCGv tmp = new_tmp(); + if (shift == 0) { + tcg_gen_andi_i32(tmp, var, 1); + } else { + tcg_gen_shri_i32(tmp, var, shift); + if (shift != 31) { + tcg_gen_andi_i32(tmp, tmp, 1); + } + } + gen_set_CF(tmp); + dead_tmp(tmp); +} + +/* Shift by immediate. Includes special handling for shift == 0. */ +static inline void gen_uc32_shift_im(TCGv var, int shiftop, int shift, + int flags) +{ + switch (shiftop) { + case 0: /* LSL */ + if (shift != 0) { + if (flags) { + shifter_out_im(var, 32 - shift); + } + tcg_gen_shli_i32(var, var, shift); + } + break; + case 1: /* LSR */ + if (shift == 0) { + if (flags) { + tcg_gen_shri_i32(var, var, 31); + gen_set_CF(var); + } + tcg_gen_movi_i32(var, 0); + } else { + if (flags) { + shifter_out_im(var, shift - 1); + } + tcg_gen_shri_i32(var, var, shift); + } + break; + case 2: /* ASR */ + if (shift == 0) { + shift = 32; + } + if (flags) { + shifter_out_im(var, shift - 1); + } + if (shift == 32) { + shift = 31; + } + tcg_gen_sari_i32(var, var, shift); + break; + case 3: /* ROR/RRX */ + if (shift != 0) { + if (flags) { + shifter_out_im(var, shift - 1); + } + tcg_gen_rotri_i32(var, var, shift); break; + } else { + TCGv tmp = load_cpu_field(CF); + if (flags) { + shifter_out_im(var, 0); + } + tcg_gen_shri_i32(var, var, 1); + tcg_gen_shli_i32(tmp, tmp, 31); + tcg_gen_or_i32(var, var, tmp); + dead_tmp(tmp); + } + } +}; + +static inline void gen_uc32_shift_reg(TCGv var, int shiftop, + TCGv shift, int flags) +{ + if (flags) { + switch (shiftop) { + case 0: + gen_helper_shl_cc(var, var, shift); + break; + case 1: + gen_helper_shr_cc(var, var, shift); + break; + case 2: + gen_helper_sar_cc(var, var, shift); + break; + case 3: + gen_helper_ror_cc(var, var, shift); + break; + } + } else { + switch (shiftop) { + case 0: + gen_helper_shl(var, var, shift); + break; + case 1: + gen_helper_shr(var, var, shift); + break; + case 2: + gen_helper_sar(var, var, shift); + break; + case 3: + tcg_gen_andi_i32(shift, shift, 0x1f); + tcg_gen_rotr_i32(var, var, shift); + break; + } + } + dead_tmp(shift); +} + +static void gen_test_cc(int cc, int label) +{ + TCGv tmp; + TCGv tmp2; + int inv; + + switch (cc) { + case 0: /* eq: Z */ + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); + break; + case 1: /* ne: !Z */ + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label); + break; + case 2: /* cs: C */ + tmp = load_cpu_field(CF); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label); + break; + case 3: /* cc: !C */ + tmp = load_cpu_field(CF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); + break; + case 4: /* mi: N */ + tmp = load_cpu_field(NF); + tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); + break; + case 5: /* pl: !N */ + tmp = load_cpu_field(NF); + tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); + break; + case 6: /* vs: V */ + tmp = load_cpu_field(VF); + tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); + break; + case 7: /* vc: !V */ + tmp = load_cpu_field(VF); + tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); + break; + case 8: /* hi: C && !Z */ + inv = gen_new_label(); + tmp = load_cpu_field(CF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv); + dead_tmp(tmp); + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label); + gen_set_label(inv); + break; + case 9: /* ls: !C || Z */ + tmp = load_cpu_field(CF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); + dead_tmp(tmp); + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); + break; + case 10: /* ge: N == V -> N ^ V == 0 */ + tmp = load_cpu_field(VF); + tmp2 = load_cpu_field(NF); + tcg_gen_xor_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); + break; + case 11: /* lt: N != V -> N ^ V != 0 */ + tmp = load_cpu_field(VF); + tmp2 = load_cpu_field(NF); + tcg_gen_xor_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); + break; + case 12: /* gt: !Z && N == V */ + inv = gen_new_label(); + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv); + dead_tmp(tmp); + tmp = load_cpu_field(VF); + tmp2 = load_cpu_field(NF); + tcg_gen_xor_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label); + gen_set_label(inv); + break; + case 13: /* le: Z || N != V */ + tmp = load_cpu_field(ZF); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label); + dead_tmp(tmp); + tmp = load_cpu_field(VF); + tmp2 = load_cpu_field(NF); + tcg_gen_xor_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label); + break; + default: + fprintf(stderr, "Bad condition code 0x%x\n", cc); + abort(); + } + dead_tmp(tmp); +} + +static const uint8_t table_logic_cc[16] = { + 1, /* and */ 1, /* xor */ 0, /* sub */ 0, /* rsb */ + 0, /* add */ 0, /* adc */ 0, /* sbc */ 0, /* rsc */ + 1, /* andl */ 1, /* xorl */ 0, /* cmp */ 0, /* cmn */ + 1, /* orr */ 1, /* mov */ 1, /* bic */ 1, /* mvn */ +}; + +/* Set PC state from an immediate address. */ +static inline void gen_bx_im(DisasContext *s, uint32_t addr) +{ + s->is_jmp = DISAS_UPDATE; + tcg_gen_movi_i32(cpu_R[31], addr & ~3); +} + +/* Set PC state from var. var is marked as dead. */ +static inline void gen_bx(DisasContext *s, TCGv var) +{ + s->is_jmp = DISAS_UPDATE; + tcg_gen_andi_i32(cpu_R[31], var, ~3); + dead_tmp(var); +} + +static inline void store_reg_bx(DisasContext *s, int reg, TCGv var) +{ + store_reg(s, reg, var); +} + +static inline TCGv gen_ld8s(TCGv addr, int index) +{ + TCGv tmp = new_tmp(); + tcg_gen_qemu_ld8s(tmp, addr, index); + return tmp; +} + +static inline TCGv gen_ld8u(TCGv addr, int index) +{ + TCGv tmp = new_tmp(); + tcg_gen_qemu_ld8u(tmp, addr, index); + return tmp; +} + +static inline TCGv gen_ld16s(TCGv addr, int index) +{ + TCGv tmp = new_tmp(); + tcg_gen_qemu_ld16s(tmp, addr, index); + return tmp; +} + +static inline TCGv gen_ld16u(TCGv addr, int index) +{ + TCGv tmp = new_tmp(); + tcg_gen_qemu_ld16u(tmp, addr, index); + return tmp; +} + +static inline TCGv gen_ld32(TCGv addr, int index) +{ + TCGv tmp = new_tmp(); + tcg_gen_qemu_ld32u(tmp, addr, index); + return tmp; +} + +static inline TCGv_i64 gen_ld64(TCGv addr, int index) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + tcg_gen_qemu_ld64(tmp, addr, index); + return tmp; +} + +static inline void gen_st8(TCGv val, TCGv addr, int index) +{ + tcg_gen_qemu_st8(val, addr, index); + dead_tmp(val); +} + +static inline void gen_st16(TCGv val, TCGv addr, int index) +{ + tcg_gen_qemu_st16(val, addr, index); + dead_tmp(val); +} + +static inline void gen_st32(TCGv val, TCGv addr, int index) +{ + tcg_gen_qemu_st32(val, addr, index); + dead_tmp(val); +} + +static inline void gen_st64(TCGv_i64 val, TCGv addr, int index) +{ + tcg_gen_qemu_st64(val, addr, index); + tcg_temp_free_i64(val); +} + +static inline void gen_set_pc_im(uint32_t val) +{ + tcg_gen_movi_i32(cpu_R[31], val); +} + +/* Force a TB lookup after an instruction that changes the CPU state. */ +static inline void gen_lookup_tb(DisasContext *s) +{ + tcg_gen_movi_i32(cpu_R[31], s->pc & ~1); + s->is_jmp = DISAS_UPDATE; +} + +static inline void gen_add_data_offset(DisasContext *s, unsigned int insn, + TCGv var) +{ + int val; + TCGv offset; + + if (UCOP_SET(29)) { + /* immediate */ + val = UCOP_IMM14; + if (!UCOP_SET_U) { + val = -val; + } + if (val != 0) { + tcg_gen_addi_i32(var, var, val); + } + } else { + /* shift/register */ + offset = load_reg(s, UCOP_REG_M); + gen_uc32_shift_im(offset, UCOP_SH_OP, UCOP_SH_IM, 0); + if (!UCOP_SET_U) { + tcg_gen_sub_i32(var, var, offset); + } else { + tcg_gen_add_i32(var, var, offset); + } + dead_tmp(offset); + } +} + +static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn, + TCGv var) +{ + int val; + TCGv offset; + + if (UCOP_SET(26)) { + /* immediate */ + val = (insn & 0x1f) | ((insn >> 4) & 0x3e0); + if (!UCOP_SET_U) { + val = -val; + } + if (val != 0) { + tcg_gen_addi_i32(var, var, val); + } + } else { + /* register */ + offset = load_reg(s, UCOP_REG_M); + if (!UCOP_SET_U) { + tcg_gen_sub_i32(var, var, offset); + } else { + tcg_gen_add_i32(var, var, offset); + } + dead_tmp(offset); + } +} + +static inline long ucf64_reg_offset(int reg) +{ + if (reg & 1) { + return offsetof(CPUState, ucf64.regs[reg >> 1]) + + offsetof(CPU_DoubleU, l.upper); + } else { + return offsetof(CPUState, ucf64.regs[reg >> 1]) + + offsetof(CPU_DoubleU, l.lower); + } +} + +#define ucf64_gen_ld32(reg) load_cpu_offset(ucf64_reg_offset(reg)) +#define ucf64_gen_st32(var, reg) store_cpu_offset(var, ucf64_reg_offset(reg)) + +/* UniCore-F64 single load/store I_offset */ +static void do_ucf64_ldst_i(CPUState *env, DisasContext *s, uint32_t insn) +{ + int offset; + TCGv tmp; + TCGv addr; + + addr = load_reg(s, UCOP_REG_N); + if (!UCOP_SET_P && !UCOP_SET_W) { + ILLEGAL; + } + + if (UCOP_SET_P) { + offset = UCOP_IMM10 << 2; + if (!UCOP_SET_U) { + offset = -offset; + } + if (offset != 0) { + tcg_gen_addi_i32(addr, addr, offset); + } + } + + if (UCOP_SET_L) { /* load */ + tmp = gen_ld32(addr, IS_USER(s)); + ucf64_gen_st32(tmp, UCOP_REG_D); + } else { /* store */ + tmp = ucf64_gen_ld32(UCOP_REG_D); + gen_st32(tmp, addr, IS_USER(s)); + } + + if (!UCOP_SET_P) { + offset = UCOP_IMM10 << 2; + if (!UCOP_SET_U) { + offset = -offset; + } + if (offset != 0) { + tcg_gen_addi_i32(addr, addr, offset); + } + } + if (UCOP_SET_W) { + store_reg(s, UCOP_REG_N, addr); + } else { + dead_tmp(addr); + } +} + +/* UniCore-F64 load/store multiple words */ +static void do_ucf64_ldst_m(CPUState *env, DisasContext *s, uint32_t insn) +{ + unsigned int i; + int j, n, freg; + TCGv tmp; + TCGv addr; + + if (UCOP_REG_D != 0) { + ILLEGAL; + } + if (UCOP_REG_N == 31) { + ILLEGAL; + } + if ((insn << 24) == 0) { + ILLEGAL; + } + + addr = load_reg(s, UCOP_REG_N); + + n = 0; + for (i = 0; i < 8; i++) { + if (UCOP_SET(i)) { + n++; + } + } + + if (UCOP_SET_U) { + if (UCOP_SET_P) { /* pre increment */ + tcg_gen_addi_i32(addr, addr, 4); + } /* unnecessary to do anything when post increment */ + } else { + if (UCOP_SET_P) { /* pre decrement */ + tcg_gen_addi_i32(addr, addr, -(n * 4)); + } else { /* post decrement */ + if (n != 1) { + tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); + } + } + } + + freg = ((insn >> 8) & 3) << 3; /* freg should be 0, 8, 16, 24 */ + + for (i = 0, j = 0; i < 8; i++, freg++) { + if (!UCOP_SET(i)) { + continue; + } + + if (UCOP_SET_L) { /* load */ + tmp = gen_ld32(addr, IS_USER(s)); + ucf64_gen_st32(tmp, freg); + } else { /* store */ + tmp = ucf64_gen_ld32(freg); + gen_st32(tmp, addr, IS_USER(s)); + } + + j++; + /* unnecessary to add after the last transfer */ + if (j != n) { + tcg_gen_addi_i32(addr, addr, 4); + } + } + + if (UCOP_SET_W) { /* write back */ + if (UCOP_SET_U) { + if (!UCOP_SET_P) { /* post increment */ + tcg_gen_addi_i32(addr, addr, 4); + } /* unnecessary to do anything when pre increment */ + } else { + if (UCOP_SET_P) { + /* pre decrement */ + if (n != 1) { + tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); + } + } else { + /* post decrement */ + tcg_gen_addi_i32(addr, addr, -(n * 4)); + } + } + store_reg(s, UCOP_REG_N, addr); + } else { + dead_tmp(addr); + } +} + +/* UniCore-F64 mrc/mcr */ +static void do_ucf64_trans(CPUState *env, DisasContext *s, uint32_t insn) +{ + TCGv tmp; + + if ((insn & 0xfe0003ff) == 0xe2000000) { + /* control register */ + if ((UCOP_REG_N != UC32_UCF64_FPSCR) || (UCOP_REG_D == 31)) { + ILLEGAL; + } + if (UCOP_SET(24)) { + /* CFF */ + tmp = new_tmp(); + gen_helper_ucf64_get_fpscr(tmp, cpu_env); + store_reg(s, UCOP_REG_D, tmp); + } else { + /* CTF */ + tmp = load_reg(s, UCOP_REG_D); + gen_helper_ucf64_set_fpscr(cpu_env, tmp); + dead_tmp(tmp); + gen_lookup_tb(s); + } + return; + } + if ((insn & 0xfe0003ff) == 0xe0000000) { + /* general register */ + if (UCOP_REG_D == 31) { + ILLEGAL; + } + if (UCOP_SET(24)) { /* MFF */ + tmp = ucf64_gen_ld32(UCOP_REG_N); + store_reg(s, UCOP_REG_D, tmp); + } else { /* MTF */ + tmp = load_reg(s, UCOP_REG_D); + ucf64_gen_st32(tmp, UCOP_REG_N); + } + return; + } + if ((insn & 0xfb000000) == 0xe9000000) { + /* MFFC */ + if (UCOP_REG_D != 31) { + ILLEGAL; + } + if (UCOP_UCF64_COND & 0x8) { + ILLEGAL; + } + + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, UCOP_UCF64_COND); + if (UCOP_SET(26)) { + tcg_gen_ld_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_N)); + tcg_gen_ld_i64(cpu_F1d, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_cmpd(cpu_F0d, cpu_F1d, tmp, cpu_env); + } else { + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_N)); + tcg_gen_ld_i32(cpu_F1s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_cmps(cpu_F0s, cpu_F1s, tmp, cpu_env); + } + dead_tmp(tmp); + return; + } + ILLEGAL; +} + +/* UniCore-F64 convert instructions */ +static void do_ucf64_fcvt(CPUState *env, DisasContext *s, uint32_t insn) +{ + if (UCOP_UCF64_FMT == 3) { + ILLEGAL; + } + if (UCOP_REG_N != 0) { + ILLEGAL; + } + switch (UCOP_UCF64_FUNC) { + case 0: /* cvt.s */ + switch (UCOP_UCF64_FMT) { + case 1 /* d */: + tcg_gen_ld_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_df2sf(cpu_F0s, cpu_F0d, cpu_env); + tcg_gen_st_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + case 2 /* w */: + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_si2sf(cpu_F0s, cpu_F0s, cpu_env); + tcg_gen_st_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + default /* s */: + ILLEGAL; + break; + } + break; + case 1: /* cvt.d */ + switch (UCOP_UCF64_FMT) { + case 0 /* s */: + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_sf2df(cpu_F0d, cpu_F0s, cpu_env); + tcg_gen_st_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + case 2 /* w */: + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_si2df(cpu_F0d, cpu_F0s, cpu_env); + tcg_gen_st_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + default /* d */: + ILLEGAL; + break; + } + break; + case 4: /* cvt.w */ + switch (UCOP_UCF64_FMT) { + case 0 /* s */: + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_sf2si(cpu_F0s, cpu_F0s, cpu_env); + tcg_gen_st_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + case 1 /* d */: + tcg_gen_ld_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + gen_helper_ucf64_df2si(cpu_F0s, cpu_F0d, cpu_env); + tcg_gen_st_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_D)); + break; + default /* w */: + ILLEGAL; + break; + } + break; + default: + ILLEGAL; + } +} + +/* UniCore-F64 compare instructions */ +static void do_ucf64_fcmp(CPUState *env, DisasContext *s, uint32_t insn) +{ + if (UCOP_SET(25)) { + ILLEGAL; + } + if (UCOP_REG_D != 0) { + ILLEGAL; + } + + ILLEGAL; /* TODO */ + if (UCOP_SET(24)) { + tcg_gen_ld_i64(cpu_F0d, cpu_env, ucf64_reg_offset(UCOP_REG_N)); + tcg_gen_ld_i64(cpu_F1d, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + /* gen_helper_ucf64_cmpd(cpu_F0d, cpu_F1d, cpu_env); */ + } else { + tcg_gen_ld_i32(cpu_F0s, cpu_env, ucf64_reg_offset(UCOP_REG_N)); + tcg_gen_ld_i32(cpu_F1s, cpu_env, ucf64_reg_offset(UCOP_REG_M)); + /* gen_helper_ucf64_cmps(cpu_F0s, cpu_F1s, cpu_env); */ + } +} + +#define gen_helper_ucf64_movs(x, y) do { } while (0) +#define gen_helper_ucf64_movd(x, y) do { } while (0) + +#define UCF64_OP1(name) do { \ + if (UCOP_REG_N != 0) { \ + ILLEGAL; \ + } \ + switch (UCOP_UCF64_FMT) { \ + case 0 /* s */: \ + tcg_gen_ld_i32(cpu_F0s, cpu_env, \ + ucf64_reg_offset(UCOP_REG_M)); \ + gen_helper_ucf64_##name##s(cpu_F0s, cpu_F0s); \ + tcg_gen_st_i32(cpu_F0s, cpu_env, \ + ucf64_reg_offset(UCOP_REG_D)); \ + break; \ + case 1 /* d */: \ + tcg_gen_ld_i64(cpu_F0d, cpu_env, \ + ucf64_reg_offset(UCOP_REG_M)); \ + gen_helper_ucf64_##name##d(cpu_F0d, cpu_F0d); \ + tcg_gen_st_i64(cpu_F0d, cpu_env, \ + ucf64_reg_offset(UCOP_REG_D)); \ + break; \ + case 2 /* w */: \ + ILLEGAL; \ + break; \ + } \ + } while (0) + +#define UCF64_OP2(name) do { \ + switch (UCOP_UCF64_FMT) { \ + case 0 /* s */: \ + tcg_gen_ld_i32(cpu_F0s, cpu_env, \ + ucf64_reg_offset(UCOP_REG_N)); \ + tcg_gen_ld_i32(cpu_F1s, cpu_env, \ + ucf64_reg_offset(UCOP_REG_M)); \ + gen_helper_ucf64_##name##s(cpu_F0s, \ + cpu_F0s, cpu_F1s, cpu_env); \ + tcg_gen_st_i32(cpu_F0s, cpu_env, \ + ucf64_reg_offset(UCOP_REG_D)); \ + break; \ + case 1 /* d */: \ + tcg_gen_ld_i64(cpu_F0d, cpu_env, \ + ucf64_reg_offset(UCOP_REG_N)); \ + tcg_gen_ld_i64(cpu_F1d, cpu_env, \ + ucf64_reg_offset(UCOP_REG_M)); \ + gen_helper_ucf64_##name##d(cpu_F0d, \ + cpu_F0d, cpu_F1d, cpu_env); \ + tcg_gen_st_i64(cpu_F0d, cpu_env, \ + ucf64_reg_offset(UCOP_REG_D)); \ + break; \ + case 2 /* w */: \ + ILLEGAL; \ + break; \ + } \ + } while (0) + +/* UniCore-F64 data processing */ +static void do_ucf64_datap(CPUState *env, DisasContext *s, uint32_t insn) +{ + if (UCOP_UCF64_FMT == 3) { + ILLEGAL; + } + switch (UCOP_UCF64_FUNC) { + case 0: /* add */ + UCF64_OP2(add); + break; + case 1: /* sub */ + UCF64_OP2(sub); + break; + case 2: /* mul */ + UCF64_OP2(mul); + break; + case 4: /* div */ + UCF64_OP2(div); + break; + case 5: /* abs */ + UCF64_OP1(abs); + break; + case 6: /* mov */ + UCF64_OP1(mov); + break; + case 7: /* neg */ + UCF64_OP1(neg); + break; + default: + ILLEGAL; + } +} + +/* Disassemble an F64 instruction */ +static void disas_ucf64_insn(CPUState *env, DisasContext *s, uint32_t insn) +{ + if (!UCOP_SET(29)) { + if (UCOP_SET(26)) { + do_ucf64_ldst_m(env, s, insn); + } else { + do_ucf64_ldst_i(env, s, insn); + } + } else { + if (UCOP_SET(5)) { + switch ((insn >> 26) & 0x3) { + case 0: + do_ucf64_datap(env, s, insn); + break; + case 1: + ILLEGAL; + break; + case 2: + do_ucf64_fcvt(env, s, insn); + break; + case 3: + do_ucf64_fcmp(env, s, insn); + break; + } + } else { + do_ucf64_trans(env, s, insn); + } + } +} + +static inline void gen_goto_tb(DisasContext *s, int n, uint32_t dest) +{ + TranslationBlock *tb; + + tb = s->tb; + if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) { + tcg_gen_goto_tb(n); + gen_set_pc_im(dest); + tcg_gen_exit_tb((long)tb + n); + } else { + gen_set_pc_im(dest); + tcg_gen_exit_tb(0); + } +} + +static inline void gen_jmp(DisasContext *s, uint32_t dest) +{ + if (unlikely(s->singlestep_enabled)) { + /* An indirect jump so that we still trigger the debug exception. */ + gen_bx_im(s, dest); + } else { + gen_goto_tb(s, 0, dest); + s->is_jmp = DISAS_TB_JUMP; + } +} + +static inline void gen_mulxy(TCGv t0, TCGv t1, int x, int y) +{ + if (x) { + tcg_gen_sari_i32(t0, t0, 16); + } else { + gen_sxth(t0); + } + if (y) { + tcg_gen_sari_i32(t1, t1, 16); + } else { + gen_sxth(t1); + } + tcg_gen_mul_i32(t0, t0, t1); +} + +/* Returns nonzero if access to the PSR is not permitted. Marks t0 as dead. */ +static int gen_set_psr(DisasContext *s, uint32_t mask, int bsr, TCGv t0) +{ + TCGv tmp; + if (bsr) { + /* ??? This is also undefined in system mode. */ + if (IS_USER(s)) { + return 1; + } + + tmp = load_cpu_field(bsr); + tcg_gen_andi_i32(tmp, tmp, ~mask); + tcg_gen_andi_i32(t0, t0, mask); + tcg_gen_or_i32(tmp, tmp, t0); + store_cpu_field(tmp, bsr); + } else { + gen_set_asr(t0, mask); + } + dead_tmp(t0); + gen_lookup_tb(s); + return 0; +} + +/* Generate an old-style exception return. Marks pc as dead. */ +static void gen_exception_return(DisasContext *s, TCGv pc) +{ + TCGv tmp; + store_reg(s, 31, pc); + tmp = load_cpu_field(bsr); + gen_set_asr(tmp, 0xffffffff); + dead_tmp(tmp); + s->is_jmp = DISAS_UPDATE; +} + +static void disas_coproc_insn(CPUState *env, DisasContext *s, uint32_t insn) +{ + switch (UCOP_CPNUM) { + case 2: + disas_ucf64_insn(env, s, insn); + break; + default: + /* Unknown coprocessor. */ + cpu_abort(env, "Unknown coprocessor!"); + } +} + + +/* Store a 64-bit value to a register pair. Clobbers val. */ +static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val) +{ + TCGv tmp; + tmp = new_tmp(); + tcg_gen_trunc_i64_i32(tmp, val); + store_reg(s, rlow, tmp); + tmp = new_tmp(); + tcg_gen_shri_i64(val, val, 32); + tcg_gen_trunc_i64_i32(tmp, val); + store_reg(s, rhigh, tmp); +} + +/* load and add a 64-bit value from a register pair. */ +static void gen_addq(DisasContext *s, TCGv_i64 val, int rlow, int rhigh) +{ + TCGv_i64 tmp; + TCGv tmpl; + TCGv tmph; + + /* Load 64-bit value rd:rn. */ + tmpl = load_reg(s, rlow); + tmph = load_reg(s, rhigh); + tmp = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(tmp, tmpl, tmph); + dead_tmp(tmpl); + dead_tmp(tmph); + tcg_gen_add_i64(val, val, tmp); + tcg_temp_free_i64(tmp); +} + +/* data processing instructions */ +static void do_datap(CPUState *env, DisasContext *s, uint32_t insn) +{ + TCGv tmp; + TCGv tmp2; + int logic_cc; + + if (UCOP_OPCODES == 0x0f || UCOP_OPCODES == 0x0d) { + if (UCOP_SET(23)) { /* CMOV instructions */ + if ((UCOP_CMOV_COND == 0xe) || (UCOP_CMOV_COND == 0xf)) { + ILLEGAL; + } + /* if not always execute, we generate a conditional jump to + next instruction */ + s->condlabel = gen_new_label(); + gen_test_cc(UCOP_CMOV_COND ^ 1, s->condlabel); + s->condjmp = 1; + } + } + + logic_cc = table_logic_cc[UCOP_OPCODES] & (UCOP_SET_S >> 24); + + if (UCOP_SET(29)) { + unsigned int val; + /* immediate operand */ + val = UCOP_IMM_9; + if (UCOP_SH_IM) { + val = (val >> UCOP_SH_IM) | (val << (32 - UCOP_SH_IM)); + } + tmp2 = new_tmp(); + tcg_gen_movi_i32(tmp2, val); + if (logic_cc && UCOP_SH_IM) { + gen_set_CF_bit31(tmp2); + } + } else { + /* register */ + tmp2 = load_reg(s, UCOP_REG_M); + if (UCOP_SET(5)) { + tmp = load_reg(s, UCOP_REG_S); + gen_uc32_shift_reg(tmp2, UCOP_SH_OP, tmp, logic_cc); + } else { + gen_uc32_shift_im(tmp2, UCOP_SH_OP, UCOP_SH_IM, logic_cc); + } + } + + if (UCOP_OPCODES != 0x0f && UCOP_OPCODES != 0x0d) { + tmp = load_reg(s, UCOP_REG_N); + } else { + TCGV_UNUSED(tmp); + } + + switch (UCOP_OPCODES) { + case 0x00: + tcg_gen_and_i32(tmp, tmp, tmp2); + if (logic_cc) { + gen_logic_CC(tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x01: + tcg_gen_xor_i32(tmp, tmp, tmp2); + if (logic_cc) { + gen_logic_CC(tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x02: + if (UCOP_SET_S && UCOP_REG_D == 31) { + /* SUBS r31, ... is used for exception return. */ + if (IS_USER(s)) { + ILLEGAL; + } + gen_helper_sub_cc(tmp, tmp, tmp2); + gen_exception_return(s, tmp); + } else { + if (UCOP_SET_S) { + gen_helper_sub_cc(tmp, tmp, tmp2); + } else { + tcg_gen_sub_i32(tmp, tmp, tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp); + } + break; + case 0x03: + if (UCOP_SET_S) { + gen_helper_sub_cc(tmp, tmp2, tmp); + } else { + tcg_gen_sub_i32(tmp, tmp2, tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x04: + if (UCOP_SET_S) { + gen_helper_add_cc(tmp, tmp, tmp2); + } else { + tcg_gen_add_i32(tmp, tmp, tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x05: + if (UCOP_SET_S) { + gen_helper_adc_cc(tmp, tmp, tmp2); + } else { + gen_add_carry(tmp, tmp, tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x06: + if (UCOP_SET_S) { + gen_helper_sbc_cc(tmp, tmp, tmp2); + } else { + gen_sub_carry(tmp, tmp, tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x07: + if (UCOP_SET_S) { + gen_helper_sbc_cc(tmp, tmp2, tmp); + } else { + gen_sub_carry(tmp, tmp2, tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x08: + if (UCOP_SET_S) { + tcg_gen_and_i32(tmp, tmp, tmp2); + gen_logic_CC(tmp); + } + dead_tmp(tmp); + break; + case 0x09: + if (UCOP_SET_S) { + tcg_gen_xor_i32(tmp, tmp, tmp2); + gen_logic_CC(tmp); + } + dead_tmp(tmp); + break; + case 0x0a: + if (UCOP_SET_S) { + gen_helper_sub_cc(tmp, tmp, tmp2); + } + dead_tmp(tmp); + break; + case 0x0b: + if (UCOP_SET_S) { + gen_helper_add_cc(tmp, tmp, tmp2); + } + dead_tmp(tmp); + break; + case 0x0c: + tcg_gen_or_i32(tmp, tmp, tmp2); + if (logic_cc) { + gen_logic_CC(tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + case 0x0d: + if (logic_cc && UCOP_REG_D == 31) { + /* MOVS r31, ... is used for exception return. */ + if (IS_USER(s)) { + ILLEGAL; + } + gen_exception_return(s, tmp2); + } else { + if (logic_cc) { + gen_logic_CC(tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp2); + } + break; + case 0x0e: + tcg_gen_andc_i32(tmp, tmp, tmp2); + if (logic_cc) { + gen_logic_CC(tmp); + } + store_reg_bx(s, UCOP_REG_D, tmp); + break; + default: + case 0x0f: + tcg_gen_not_i32(tmp2, tmp2); + if (logic_cc) { + gen_logic_CC(tmp2); + } + store_reg_bx(s, UCOP_REG_D, tmp2); + break; + } + if (UCOP_OPCODES != 0x0f && UCOP_OPCODES != 0x0d) { + dead_tmp(tmp2); + } +} + +/* multiply */ +static void do_mult(CPUState *env, DisasContext *s, uint32_t insn) +{ + TCGv tmp; + TCGv tmp2; + TCGv_i64 tmp64; + + if (UCOP_SET(27)) { + /* 64 bit mul */ + tmp = load_reg(s, UCOP_REG_M); + tmp2 = load_reg(s, UCOP_REG_N); + if (UCOP_SET(26)) { + tmp64 = gen_muls_i64_i32(tmp, tmp2); + } else { + tmp64 = gen_mulu_i64_i32(tmp, tmp2); + } + if (UCOP_SET(25)) { /* mult accumulate */ + gen_addq(s, tmp64, UCOP_REG_LO, UCOP_REG_HI); + } + gen_storeq_reg(s, UCOP_REG_LO, UCOP_REG_HI, tmp64); + tcg_temp_free_i64(tmp64); + } else { + /* 32 bit mul */ + tmp = load_reg(s, UCOP_REG_M); + tmp2 = load_reg(s, UCOP_REG_N); + tcg_gen_mul_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + if (UCOP_SET(25)) { + /* Add */ + tmp2 = load_reg(s, UCOP_REG_S); + tcg_gen_add_i32(tmp, tmp, tmp2); + dead_tmp(tmp2); + } + if (UCOP_SET_S) { + gen_logic_CC(tmp); + } + store_reg(s, UCOP_REG_D, tmp); + } +} + +/* miscellaneous instructions */ +static void do_misc(CPUState *env, DisasContext *s, uint32_t insn) +{ + unsigned int val; + TCGv tmp; + + if ((insn & 0xffffffe0) == 0x10ffc120) { + /* Trivial implementation equivalent to bx. */ + tmp = load_reg(s, UCOP_REG_M); + gen_bx(s, tmp); + return; + } + + if ((insn & 0xfbffc000) == 0x30ffc000) { + /* PSR = immediate */ + val = UCOP_IMM_9; + if (UCOP_SH_IM) { + val = (val >> UCOP_SH_IM) | (val << (32 - UCOP_SH_IM)); + } + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, val); + if (gen_set_psr(s, ~ASR_RESERVED, UCOP_SET_B, tmp)) { + ILLEGAL; + } + return; + } + + if ((insn & 0xfbffffe0) == 0x12ffc020) { + /* PSR.flag = reg */ + tmp = load_reg(s, UCOP_REG_M); + if (gen_set_psr(s, ASR_NZCV, UCOP_SET_B, tmp)) { + ILLEGAL; + } + return; + } + + if ((insn & 0xfbffffe0) == 0x10ffc020) { + /* PSR = reg */ + tmp = load_reg(s, UCOP_REG_M); + if (gen_set_psr(s, ~ASR_RESERVED, UCOP_SET_B, tmp)) { + ILLEGAL; + } + return; + } + + if ((insn & 0xfbf83fff) == 0x10f80000) { + /* reg = PSR */ + if (UCOP_SET_B) { + if (IS_USER(s)) { + ILLEGAL; + } + tmp = load_cpu_field(bsr); + } else { + tmp = new_tmp(); + gen_helper_asr_read(tmp); + } + store_reg(s, UCOP_REG_D, tmp); + return; + } + + if ((insn & 0xfbf83fe0) == 0x12f80120) { + /* clz */ + tmp = load_reg(s, UCOP_REG_M); + if (UCOP_SET(26)) { + gen_helper_clo(tmp, tmp); + } else { + gen_helper_clz(tmp, tmp); + } + store_reg(s, UCOP_REG_D, tmp); + return; + } + + /* otherwise */ + ILLEGAL; +} + +/* load/store I_offset and R_offset */ +static void do_ldst_ir(CPUState *env, DisasContext *s, uint32_t insn) +{ + unsigned int i; + TCGv tmp; + TCGv tmp2; + + tmp2 = load_reg(s, UCOP_REG_N); + i = (IS_USER(s) || (!UCOP_SET_P && UCOP_SET_W)); + + /* immediate */ + if (UCOP_SET_P) { + gen_add_data_offset(s, insn, tmp2); + } + + if (UCOP_SET_L) { + /* load */ + if (UCOP_SET_B) { + tmp = gen_ld8u(tmp2, i); + } else { + tmp = gen_ld32(tmp2, i); + } + } else { + /* store */ + tmp = load_reg(s, UCOP_REG_D); + if (UCOP_SET_B) { + gen_st8(tmp, tmp2, i); + } else { + gen_st32(tmp, tmp2, i); + } + } + if (!UCOP_SET_P) { + gen_add_data_offset(s, insn, tmp2); + store_reg(s, UCOP_REG_N, tmp2); + } else if (UCOP_SET_W) { + store_reg(s, UCOP_REG_N, tmp2); + } else { + dead_tmp(tmp2); + } + if (UCOP_SET_L) { + /* Complete the load. */ + if (UCOP_REG_D == 31) { + gen_bx(s, tmp); + } else { + store_reg(s, UCOP_REG_D, tmp); + } + } +} + +/* SWP instruction */ +static void do_swap(CPUState *env, DisasContext *s, uint32_t insn) +{ + TCGv addr; + TCGv tmp; + TCGv tmp2; + + if ((insn & 0xff003fe0) != 0x40000120) { + ILLEGAL; + } + + /* ??? This is not really atomic. However we know + we never have multiple CPUs running in parallel, + so it is good enough. */ + addr = load_reg(s, UCOP_REG_N); + tmp = load_reg(s, UCOP_REG_M); + if (UCOP_SET_B) { + tmp2 = gen_ld8u(addr, IS_USER(s)); + gen_st8(tmp, addr, IS_USER(s)); + } else { + tmp2 = gen_ld32(addr, IS_USER(s)); + gen_st32(tmp, addr, IS_USER(s)); + } + dead_tmp(addr); + store_reg(s, UCOP_REG_D, tmp2); +} + +/* load/store hw/sb */ +static void do_ldst_hwsb(CPUState *env, DisasContext *s, uint32_t insn) +{ + TCGv addr; + TCGv tmp; + + if (UCOP_SH_OP == 0) { + do_swap(env, s, insn); + return; + } + + addr = load_reg(s, UCOP_REG_N); + if (UCOP_SET_P) { + gen_add_datah_offset(s, insn, addr); + } + + if (UCOP_SET_L) { /* load */ + switch (UCOP_SH_OP) { + case 1: + tmp = gen_ld16u(addr, IS_USER(s)); + break; + case 2: + tmp = gen_ld8s(addr, IS_USER(s)); + break; + default: /* see do_swap */ + case 3: + tmp = gen_ld16s(addr, IS_USER(s)); + break; + } + } else { /* store */ + if (UCOP_SH_OP != 1) { + ILLEGAL; + } + tmp = load_reg(s, UCOP_REG_D); + gen_st16(tmp, addr, IS_USER(s)); + } + /* Perform base writeback before the loaded value to + ensure correct behavior with overlapping index registers. */ + if (!UCOP_SET_P) { + gen_add_datah_offset(s, insn, addr); + store_reg(s, UCOP_REG_N, addr); + } else if (UCOP_SET_W) { + store_reg(s, UCOP_REG_N, addr); + } else { + dead_tmp(addr); + } + if (UCOP_SET_L) { + /* Complete the load. */ + store_reg(s, UCOP_REG_D, tmp); + } +} + +/* load/store multiple words */ +static void do_ldst_m(CPUState *env, DisasContext *s, uint32_t insn) +{ + unsigned int val, i; + int j, n, reg, user, loaded_base; + TCGv tmp; + TCGv tmp2; + TCGv addr; + TCGv loaded_var; + + if (UCOP_SET(7)) { + ILLEGAL; + } + /* XXX: store correct base if write back */ + user = 0; + if (UCOP_SET_B) { /* S bit in instruction table */ + if (IS_USER(s)) { + ILLEGAL; /* only usable in supervisor mode */ + } + if (UCOP_SET(18) == 0) { /* pc reg */ + user = 1; + } + } + + addr = load_reg(s, UCOP_REG_N); + + /* compute total size */ + loaded_base = 0; + TCGV_UNUSED(loaded_var); + n = 0; + for (i = 0; i < 6; i++) { + if (UCOP_SET(i)) { + n++; + } + } + for (i = 9; i < 19; i++) { + if (UCOP_SET(i)) { + n++; + } + } + /* XXX: test invalid n == 0 case ? */ + if (UCOP_SET_U) { + if (UCOP_SET_P) { + /* pre increment */ + tcg_gen_addi_i32(addr, addr, 4); + } else { + /* post increment */ + } + } else { + if (UCOP_SET_P) { + /* pre decrement */ + tcg_gen_addi_i32(addr, addr, -(n * 4)); + } else { + /* post decrement */ + if (n != 1) { + tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); + } + } + } + + j = 0; + reg = UCOP_SET(6) ? 16 : 0; + for (i = 0; i < 19; i++, reg++) { + if (i == 6) { + i = i + 3; + } + if (UCOP_SET(i)) { + if (UCOP_SET_L) { /* load */ + tmp = gen_ld32(addr, IS_USER(s)); + if (reg == 31) { + gen_bx(s, tmp); + } else if (user) { + tmp2 = tcg_const_i32(reg); + gen_helper_set_user_reg(tmp2, tmp); + tcg_temp_free_i32(tmp2); + dead_tmp(tmp); + } else if (reg == UCOP_REG_N) { + loaded_var = tmp; + loaded_base = 1; + } else { + store_reg(s, reg, tmp); + } + } else { /* store */ + if (reg == 31) { + /* special case: r31 = PC + 4 */ + val = (long)s->pc; + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, val); + } else if (user) { + tmp = new_tmp(); + tmp2 = tcg_const_i32(reg); + gen_helper_get_user_reg(tmp, tmp2); + tcg_temp_free_i32(tmp2); + } else { + tmp = load_reg(s, reg); + } + gen_st32(tmp, addr, IS_USER(s)); + } + j++; + /* no need to add after the last transfer */ + if (j != n) { + tcg_gen_addi_i32(addr, addr, 4); + } + } + } + if (UCOP_SET_W) { /* write back */ + if (UCOP_SET_U) { + if (UCOP_SET_P) { + /* pre increment */ + } else { + /* post increment */ + tcg_gen_addi_i32(addr, addr, 4); + } + } else { + if (UCOP_SET_P) { + /* pre decrement */ + if (n != 1) { + tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); + } + } else { + /* post decrement */ + tcg_gen_addi_i32(addr, addr, -(n * 4)); + } + } + store_reg(s, UCOP_REG_N, addr); + } else { + dead_tmp(addr); + } + if (loaded_base) { + store_reg(s, UCOP_REG_N, loaded_var); + } + if (UCOP_SET_B && !user) { + /* Restore ASR from BSR. */ + tmp = load_cpu_field(bsr); + gen_set_asr(tmp, 0xffffffff); + dead_tmp(tmp); + s->is_jmp = DISAS_UPDATE; + } +} + +/* branch (and link) */ +static void do_branch(CPUState *env, DisasContext *s, uint32_t insn) +{ + unsigned int val; + int32_t offset; + TCGv tmp; + + if (UCOP_COND == 0xf) { + ILLEGAL; + } + + if (UCOP_COND != 0xe) { + /* if not always execute, we generate a conditional jump to + next instruction */ + s->condlabel = gen_new_label(); + gen_test_cc(UCOP_COND ^ 1, s->condlabel); + s->condjmp = 1; + } + + val = (int32_t)s->pc; + if (UCOP_SET_L) { + tmp = new_tmp(); + tcg_gen_movi_i32(tmp, val); + store_reg(s, 30, tmp); + } + offset = (((int32_t)insn << 8) >> 8); + val += (offset << 2); /* unicore is pc+4 */ + gen_jmp(s, val); +} + +static void disas_uc32_insn(CPUState *env, DisasContext *s) +{ + unsigned int insn; + + insn = ldl_code(s->pc); + s->pc += 4; + + /* UniCore instructions class: + * AAAB BBBC xxxx xxxx xxxx xxxD xxEx xxxx + * AAA : see switch case + * BBBB : opcodes or cond or PUBW + * C : S OR L + * D : 8 + * E : 5 + */ + switch (insn >> 29) { + case 0b000: + if (UCOP_SET(5) && UCOP_SET(8) && !UCOP_SET(28)) { + do_mult(env, s, insn); + break; + } + + if (UCOP_SET(8)) { + do_misc(env, s, insn); + break; + } + case 0b001: + if (((UCOP_OPCODES >> 2) == 2) && !UCOP_SET_S) { + do_misc(env, s, insn); + break; + } + do_datap(env, s, insn); + break; + + case 0b010: + if (UCOP_SET(8) && UCOP_SET(5)) { + do_ldst_hwsb(env, s, insn); + break; + } + if (UCOP_SET(8) || UCOP_SET(5)) { + ILLEGAL; + } + case 0b011: + do_ldst_ir(env, s, insn); + break; + + case 0b100: + if (UCOP_SET(8)) { + ILLEGAL; /* extended instructions */ + } + do_ldst_m(env, s, insn); + break; + case 0b101: + do_branch(env, s, insn); + break; + case 0b110: + /* Coprocessor. */ + disas_coproc_insn(env, s, insn); + break; + case 0b111: + if (!UCOP_SET(28)) { + disas_coproc_insn(env, s, insn); + break; + } + if ((insn & 0xff000000) == 0xff000000) { /* syscall */ + gen_set_pc_im(s->pc); + s->is_jmp = DISAS_SYSCALL; + break; + } + ILLEGAL; + } + + return; +} + +/* generate intermediate code in gen_opc_buf and gen_opparam_buf for + basic block 'tb'. If search_pc is TRUE, also generate PC + information for each intermediate instruction. */ +static inline void gen_intermediate_code_internal(CPUState *env, + TranslationBlock *tb, int search_pc) +{ + DisasContext dc1, *dc = &dc1; + CPUBreakpoint *bp; + uint16_t *gen_opc_end; + int j, lj; + target_ulong pc_start; + uint32_t next_page_start; + int num_insns; + int max_insns; + + /* generate intermediate code */ + num_temps = 0; + + pc_start = tb->pc; + + dc->tb = tb; + + gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + + dc->is_jmp = DISAS_NEXT; + dc->pc = pc_start; + dc->singlestep_enabled = env->singlestep_enabled; + dc->condjmp = 0; + cpu_F0s = tcg_temp_new_i32(); + cpu_F1s = tcg_temp_new_i32(); + cpu_F0d = tcg_temp_new_i64(); + cpu_F1d = tcg_temp_new_i64(); + next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; + lj = -1; + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + + gen_icount_start(); + do { + if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { + QTAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == dc->pc) { + gen_set_pc_im(dc->pc); + gen_exception(EXCP_DEBUG); + dc->is_jmp = DISAS_JUMP; + /* Advance PC so that clearing the breakpoint will + invalidate this TB. */ + dc->pc += 2; /* FIXME */ + goto done_generating; + break; + } + } + } + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + if (lj < j) { + lj++; + while (lj < j) { + gen_opc_instr_start[lj++] = 0; + } + } + gen_opc_pc[lj] = dc->pc; + gen_opc_instr_start[lj] = 1; + gen_opc_icount[lj] = num_insns; + } + + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + + disas_uc32_insn(env, dc); + + if (num_temps) { + fprintf(stderr, "Internal resource leak before %08x\n", dc->pc); + num_temps = 0; + } + + if (dc->condjmp && !dc->is_jmp) { + gen_set_label(dc->condlabel); + dc->condjmp = 0; + } + /* Translation stops when a conditional branch is encountered. + * Otherwise the subsequent code could get translated several times. + * Also stop translation when a page boundary is reached. This + * ensures prefetch aborts occur at the right place. */ + num_insns++; + } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end && + !env->singlestep_enabled && + !singlestep && + dc->pc < next_page_start && + num_insns < max_insns); + + if (tb->cflags & CF_LAST_IO) { + if (dc->condjmp) { + /* FIXME: This can theoretically happen with self-modifying + code. */ + cpu_abort(env, "IO on conditional branch instruction"); + } + gen_io_end(); + } + + /* At this stage dc->condjmp will only be set when the skipped + instruction was a conditional branch or trap, and the PC has + already been written. */ + if (unlikely(env->singlestep_enabled)) { + /* Make sure the pc is updated, and raise a debug exception. */ + if (dc->condjmp) { + if (dc->is_jmp == DISAS_SYSCALL) { + gen_exception(UC32_EXCP_PRIV); + } else { + gen_exception(EXCP_DEBUG); + } + gen_set_label(dc->condlabel); + } + if (dc->condjmp || !dc->is_jmp) { + gen_set_pc_im(dc->pc); + dc->condjmp = 0; + } + if (dc->is_jmp == DISAS_SYSCALL && !dc->condjmp) { + gen_exception(UC32_EXCP_PRIV); + } else { + gen_exception(EXCP_DEBUG); + } + } else { + /* While branches must always occur at the end of an IT block, + there are a few other things that can cause us to terminate + the TB in the middel of an IT block: + - Exception generating instructions (bkpt, swi, undefined). + - Page boundaries. + - Hardware watchpoints. + Hardware breakpoints have already been handled and skip this code. + */ + switch (dc->is_jmp) { + case DISAS_NEXT: + gen_goto_tb(dc, 1, dc->pc); + break; + default: + case DISAS_JUMP: + case DISAS_UPDATE: + /* indicate that the hash table must be used to find the next TB */ + tcg_gen_exit_tb(0); + break; + case DISAS_TB_JUMP: + /* nothing more to generate */ + break; + case DISAS_SYSCALL: + gen_exception(UC32_EXCP_PRIV); + break; + } + if (dc->condjmp) { + gen_set_label(dc->condlabel); + gen_goto_tb(dc, 1, dc->pc); + dc->condjmp = 0; + } + } + +done_generating: + gen_icount_end(tb, num_insns); + *gen_opc_ptr = INDEX_op_end; + +#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(pc_start, dc->pc - pc_start, 0); + qemu_log("\n"); + } +#endif + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + lj++; + while (lj <= j) { + gen_opc_instr_start[lj++] = 0; + } + } else { + tb->size = dc->pc - pc_start; + tb->icount = num_insns; + } +} + +void gen_intermediate_code(CPUState *env, TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 0); +} + +void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 1); +} + +static const char *cpu_mode_names[16] = { + "USER", "REAL", "INTR", "PRIV", "UM14", "UM15", "UM16", "TRAP", + "UM18", "UM19", "UM1A", "EXTN", "UM1C", "UM1D", "UM1E", "SUSR" +}; + +#define UCF64_DUMP_STATE +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, + int flags) +{ + int i; +#ifdef UCF64_DUMP_STATE + union { + uint32_t i; + float s; + } s0, s1; + CPU_DoubleU d; + /* ??? This assumes float64 and double have the same layout. + Oh well, it's only debug dumps. */ + union { + float64 f64; + double d; + } d0; +#endif + uint32_t psr; + + for (i = 0; i < 32; i++) { + cpu_fprintf(f, "R%02d=%08x", i, env->regs[i]); + if ((i % 4) == 3) { + cpu_fprintf(f, "\n"); + } else { + cpu_fprintf(f, " "); + } + } + psr = cpu_asr_read(env); + cpu_fprintf(f, "PSR=%08x %c%c%c%c %s\n", + psr, + psr & (1 << 31) ? 'N' : '-', + psr & (1 << 30) ? 'Z' : '-', + psr & (1 << 29) ? 'C' : '-', + psr & (1 << 28) ? 'V' : '-', + cpu_mode_names[psr & 0xf]); + +#ifdef UCF64_DUMP_STATE + for (i = 0; i < 16; i++) { + d.d = env->ucf64.regs[i]; + s0.i = d.l.lower; + s1.i = d.l.upper; + d0.f64 = d.d; + cpu_fprintf(f, "s%02d=%08x(%8g) s%02d=%08x(%8g) d%02d=%" PRIx64 "(%8g)\n", + i * 2, (int)s0.i, s0.s, + i * 2 + 1, (int)s1.i, s1.s, + i, (uint64_t)d0.f64, d0.d); + } + cpu_fprintf(f, "FPSCR: %08x\n", (int)env->ucf64.xregs[UC32_UCF64_FPSCR]); +#endif +} + +void gen_pc_load(CPUState *env, TranslationBlock *tb, + unsigned long searched_pc, int pc_pos, void *puc) +{ + env->regs[31] = gen_opc_pc[pc_pos]; +} |