diff options
Diffstat (limited to 'target/nios2/translate.c')
-rw-r--r-- | target/nios2/translate.c | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/target/nios2/translate.c b/target/nios2/translate.c new file mode 100644 index 000000000..08d7ac539 --- /dev/null +++ b/target/nios2/translate.c @@ -0,0 +1,901 @@ +/* + * Altera Nios II emulation for qemu: main translation routines. + * + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com> + * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch> + * (Portions of this file that were originally from nios2sim-ng.) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "tcg/tcg-op.h" +#include "exec/exec-all.h" +#include "disas/disas.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/log.h" +#include "exec/cpu_ldst.h" +#include "exec/translator.h" +#include "qemu/qemu-print.h" +#include "exec/gen-icount.h" + +/* is_jmp field values */ +#define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ +#define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ + +#define INSTRUCTION_FLG(func, flags) { (func), (flags) } +#define INSTRUCTION(func) \ + INSTRUCTION_FLG(func, 0) +#define INSTRUCTION_NOP() \ + INSTRUCTION_FLG(nop, 0) +#define INSTRUCTION_UNIMPLEMENTED() \ + INSTRUCTION_FLG(gen_excp, EXCP_UNIMPL) +#define INSTRUCTION_ILLEGAL() \ + INSTRUCTION_FLG(gen_excp, EXCP_ILLEGAL) + +/* Special R-Type instruction opcode */ +#define INSN_R_TYPE 0x3A + +/* I-Type instruction parsing */ +#define I_TYPE(instr, code) \ + struct { \ + uint8_t op; \ + union { \ + uint16_t u; \ + int16_t s; \ + } imm16; \ + uint8_t b; \ + uint8_t a; \ + } (instr) = { \ + .op = extract32((code), 0, 6), \ + .imm16.u = extract32((code), 6, 16), \ + .b = extract32((code), 22, 5), \ + .a = extract32((code), 27, 5), \ + } + +/* R-Type instruction parsing */ +#define R_TYPE(instr, code) \ + struct { \ + uint8_t op; \ + uint8_t imm5; \ + uint8_t opx; \ + uint8_t c; \ + uint8_t b; \ + uint8_t a; \ + } (instr) = { \ + .op = extract32((code), 0, 6), \ + .imm5 = extract32((code), 6, 5), \ + .opx = extract32((code), 11, 6), \ + .c = extract32((code), 17, 5), \ + .b = extract32((code), 22, 5), \ + .a = extract32((code), 27, 5), \ + } + +/* J-Type instruction parsing */ +#define J_TYPE(instr, code) \ + struct { \ + uint8_t op; \ + uint32_t imm26; \ + } (instr) = { \ + .op = extract32((code), 0, 6), \ + .imm26 = extract32((code), 6, 26), \ + } + +typedef struct DisasContext { + DisasContextBase base; + TCGv_i32 zero; + target_ulong pc; + int mem_idx; +} DisasContext; + +static TCGv cpu_R[NUM_CORE_REGS]; + +typedef struct Nios2Instruction { + void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags); + uint32_t flags; +} Nios2Instruction; + +static uint8_t get_opcode(uint32_t code) +{ + I_TYPE(instr, code); + return instr.op; +} + +static uint8_t get_opxcode(uint32_t code) +{ + R_TYPE(instr, code); + return instr.opx; +} + +static TCGv load_zero(DisasContext *dc) +{ + if (!dc->zero) { + dc->zero = tcg_const_i32(0); + } + return dc->zero; +} + +static TCGv load_gpr(DisasContext *dc, uint8_t reg) +{ + if (likely(reg != R_ZERO)) { + return cpu_R[reg]; + } else { + return load_zero(dc); + } +} + +static void t_gen_helper_raise_exception(DisasContext *dc, + uint32_t index) +{ + TCGv_i32 tmp = tcg_const_i32(index); + + tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); + gen_helper_raise_exception(cpu_env, tmp); + tcg_temp_free_i32(tmp); + dc->base.is_jmp = DISAS_NORETURN; +} + +static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) +{ + const TranslationBlock *tb = dc->base.tb; + + if (translator_use_goto_tb(&dc->base, dest)) { + tcg_gen_goto_tb(n); + tcg_gen_movi_tl(cpu_R[R_PC], dest); + tcg_gen_exit_tb(tb, n); + } else { + tcg_gen_movi_tl(cpu_R[R_PC], dest); + tcg_gen_exit_tb(NULL, 0); + } +} + +static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) +{ + t_gen_helper_raise_exception(dc, flags); +} + +static void gen_check_supervisor(DisasContext *dc) +{ + if (dc->base.tb->flags & CR_STATUS_U) { + /* CPU in user mode, privileged instruction called, stop. */ + t_gen_helper_raise_exception(dc, EXCP_SUPERI); + } +} + +/* + * Used as a placeholder for all instructions which do not have + * an effect on the simulator (e.g. flush, sync) + */ +static void nop(DisasContext *dc, uint32_t code, uint32_t flags) +{ + /* Nothing to do here */ +} + +/* + * J-Type instructions + */ +static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags) +{ + J_TYPE(instr, code); + gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2)); + dc->base.is_jmp = DISAS_NORETURN; +} + +static void call(DisasContext *dc, uint32_t code, uint32_t flags) +{ + tcg_gen_movi_tl(cpu_R[R_RA], dc->base.pc_next); + jmpi(dc, code, flags); +} + +/* + * I-Type instructions + */ +/* Load instructions */ +static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags) +{ + I_TYPE(instr, code); + + TCGv addr = tcg_temp_new(); + TCGv data; + + /* + * WARNING: Loads into R_ZERO are ignored, but we must generate the + * memory access itself to emulate the CPU precisely. Load + * from a protected page to R_ZERO will cause SIGSEGV on + * the Nios2 CPU. + */ + if (likely(instr.b != R_ZERO)) { + data = cpu_R[instr.b]; + } else { + data = tcg_temp_new(); + } + + tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); + tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); + + if (unlikely(instr.b == R_ZERO)) { + tcg_temp_free(data); + } + + tcg_temp_free(addr); +} + +/* Store instructions */ +static void gen_stx(DisasContext *dc, uint32_t code, uint32_t flags) +{ + I_TYPE(instr, code); + TCGv val = load_gpr(dc, instr.b); + + TCGv addr = tcg_temp_new(); + tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); + tcg_gen_qemu_st_tl(val, addr, dc->mem_idx, flags); + tcg_temp_free(addr); +} + +/* Branch instructions */ +static void br(DisasContext *dc, uint32_t code, uint32_t flags) +{ + I_TYPE(instr, code); + + gen_goto_tb(dc, 0, dc->base.pc_next + (instr.imm16.s & -4)); + dc->base.is_jmp = DISAS_NORETURN; +} + +static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags) +{ + I_TYPE(instr, code); + + TCGLabel *l1 = gen_new_label(); + tcg_gen_brcond_tl(flags, cpu_R[instr.a], cpu_R[instr.b], l1); + gen_goto_tb(dc, 0, dc->base.pc_next); + gen_set_label(l1); + gen_goto_tb(dc, 1, dc->base.pc_next + (instr.imm16.s & -4)); + dc->base.is_jmp = DISAS_NORETURN; +} + +/* Comparison instructions */ +#define gen_i_cmpxx(fname, op3) \ +static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ +{ \ + I_TYPE(instr, (code)); \ + tcg_gen_setcondi_tl(flags, cpu_R[instr.b], cpu_R[instr.a], (op3)); \ +} + +gen_i_cmpxx(gen_cmpxxsi, instr.imm16.s) +gen_i_cmpxx(gen_cmpxxui, instr.imm16.u) + +/* Math/logic instructions */ +#define gen_i_math_logic(fname, insn, resimm, op3) \ +static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ +{ \ + I_TYPE(instr, (code)); \ + if (unlikely(instr.b == R_ZERO)) { /* Store to R_ZERO is ignored */ \ + return; \ + } else if (instr.a == R_ZERO) { /* MOVxI optimizations */ \ + tcg_gen_movi_tl(cpu_R[instr.b], (resimm) ? (op3) : 0); \ + } else { \ + tcg_gen_##insn##_tl(cpu_R[instr.b], cpu_R[instr.a], (op3)); \ + } \ +} + +gen_i_math_logic(addi, addi, 1, instr.imm16.s) +gen_i_math_logic(muli, muli, 0, instr.imm16.s) + +gen_i_math_logic(andi, andi, 0, instr.imm16.u) +gen_i_math_logic(ori, ori, 1, instr.imm16.u) +gen_i_math_logic(xori, xori, 1, instr.imm16.u) + +gen_i_math_logic(andhi, andi, 0, instr.imm16.u << 16) +gen_i_math_logic(orhi , ori, 1, instr.imm16.u << 16) +gen_i_math_logic(xorhi, xori, 1, instr.imm16.u << 16) + +/* Prototype only, defined below */ +static void handle_r_type_instr(DisasContext *dc, uint32_t code, + uint32_t flags); + +static const Nios2Instruction i_type_instructions[] = { + INSTRUCTION(call), /* call */ + INSTRUCTION(jmpi), /* jmpi */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbu */ + INSTRUCTION(addi), /* addi */ + INSTRUCTION_FLG(gen_stx, MO_UB), /* stb */ + INSTRUCTION(br), /* br */ + INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldb */ + INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_GE), /* cmpgei */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhu */ + INSTRUCTION(andi), /* andi */ + INSTRUCTION_FLG(gen_stx, MO_UW), /* sth */ + INSTRUCTION_FLG(gen_bxx, TCG_COND_GE), /* bge */ + INSTRUCTION_FLG(gen_ldx, MO_SW), /* ldh */ + INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_LT), /* cmplti */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_NOP(), /* initda */ + INSTRUCTION(ori), /* ori */ + INSTRUCTION_FLG(gen_stx, MO_UL), /* stw */ + INSTRUCTION_FLG(gen_bxx, TCG_COND_LT), /* blt */ + INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldw */ + INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_NE), /* cmpnei */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_NOP(), /* flushda */ + INSTRUCTION(xori), /* xori */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_bxx, TCG_COND_NE), /* bne */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_EQ), /* cmpeqi */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbuio */ + INSTRUCTION(muli), /* muli */ + INSTRUCTION_FLG(gen_stx, MO_UB), /* stbio */ + INSTRUCTION_FLG(gen_bxx, TCG_COND_EQ), /* beq */ + INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldbio */ + INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_GEU), /* cmpgeui */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhuio */ + INSTRUCTION(andhi), /* andhi */ + INSTRUCTION_FLG(gen_stx, MO_UW), /* sthio */ + INSTRUCTION_FLG(gen_bxx, TCG_COND_GEU), /* bgeu */ + INSTRUCTION_FLG(gen_ldx, MO_SW), /* ldhio */ + INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_LTU), /* cmpltui */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_UNIMPLEMENTED(), /* custom */ + INSTRUCTION_NOP(), /* initd */ + INSTRUCTION(orhi), /* orhi */ + INSTRUCTION_FLG(gen_stx, MO_SL), /* stwio */ + INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */ + INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldwio */ + INSTRUCTION_UNIMPLEMENTED(), /* rdprs */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */ + INSTRUCTION_NOP(), /* flushd */ + INSTRUCTION(xorhi), /* xorhi */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), +}; + +/* + * R-Type instructions + */ +/* + * status <- estatus + * PC <- ea + */ +static void eret(DisasContext *dc, uint32_t code, uint32_t flags) +{ + tcg_gen_mov_tl(cpu_R[CR_STATUS], cpu_R[CR_ESTATUS]); + tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_EA]); + + dc->base.is_jmp = DISAS_JUMP; +} + +/* PC <- ra */ +static void ret(DisasContext *dc, uint32_t code, uint32_t flags) +{ + tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_RA]); + + dc->base.is_jmp = DISAS_JUMP; +} + +/* PC <- ba */ +static void bret(DisasContext *dc, uint32_t code, uint32_t flags) +{ + tcg_gen_mov_tl(cpu_R[R_PC], cpu_R[R_BA]); + + dc->base.is_jmp = DISAS_JUMP; +} + +/* PC <- rA */ +static void jmp(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + + tcg_gen_mov_tl(cpu_R[R_PC], load_gpr(dc, instr.a)); + + dc->base.is_jmp = DISAS_JUMP; +} + +/* rC <- PC + 4 */ +static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + + if (likely(instr.c != R_ZERO)) { + tcg_gen_movi_tl(cpu_R[instr.c], dc->base.pc_next); + } +} + +/* + * ra <- PC + 4 + * PC <- rA + */ +static void callr(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + + tcg_gen_mov_tl(cpu_R[R_PC], load_gpr(dc, instr.a)); + tcg_gen_movi_tl(cpu_R[R_RA], dc->base.pc_next); + + dc->base.is_jmp = DISAS_JUMP; +} + +/* rC <- ctlN */ +static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + + gen_check_supervisor(dc); + + switch (instr.imm5 + CR_BASE) { + case CR_PTEADDR: + case CR_TLBACC: + case CR_TLBMISC: + { +#if !defined(CONFIG_USER_ONLY) + if (likely(instr.c != R_ZERO)) { + tcg_gen_mov_tl(cpu_R[instr.c], cpu_R[instr.imm5 + CR_BASE]); +#ifdef DEBUG_MMU + TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE); + gen_helper_mmu_read_debug(cpu_R[instr.c], cpu_env, tmp); + tcg_temp_free_i32(tmp); +#endif + } +#endif + break; + } + + default: + if (likely(instr.c != R_ZERO)) { + tcg_gen_mov_tl(cpu_R[instr.c], cpu_R[instr.imm5 + CR_BASE]); + } + break; + } +} + +/* ctlN <- rA */ +static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + + gen_check_supervisor(dc); + + switch (instr.imm5 + CR_BASE) { + case CR_PTEADDR: + case CR_TLBACC: + case CR_TLBMISC: + { +#if !defined(CONFIG_USER_ONLY) + TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE); + gen_helper_mmu_write(cpu_env, tmp, load_gpr(dc, instr.a)); + tcg_temp_free_i32(tmp); +#endif + break; + } + + default: + tcg_gen_mov_tl(cpu_R[instr.imm5 + CR_BASE], load_gpr(dc, instr.a)); + break; + } + + /* If interrupts were enabled using WRCTL, trigger them. */ +#if !defined(CONFIG_USER_ONLY) + if ((instr.imm5 + CR_BASE) == CR_STATUS) { + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_check_interrupts(cpu_env); + dc->base.is_jmp = DISAS_UPDATE; + } +#endif +} + +/* Comparison instructions */ +static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + if (likely(instr.c != R_ZERO)) { + tcg_gen_setcond_tl(flags, cpu_R[instr.c], cpu_R[instr.a], + cpu_R[instr.b]); + } +} + +/* Math/logic instructions */ +#define gen_r_math_logic(fname, insn, op3) \ +static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ +{ \ + R_TYPE(instr, (code)); \ + if (likely(instr.c != R_ZERO)) { \ + tcg_gen_##insn(cpu_R[instr.c], load_gpr((dc), instr.a), (op3)); \ + } \ +} + +gen_r_math_logic(add, add_tl, load_gpr(dc, instr.b)) +gen_r_math_logic(sub, sub_tl, load_gpr(dc, instr.b)) +gen_r_math_logic(mul, mul_tl, load_gpr(dc, instr.b)) + +gen_r_math_logic(and, and_tl, load_gpr(dc, instr.b)) +gen_r_math_logic(or, or_tl, load_gpr(dc, instr.b)) +gen_r_math_logic(xor, xor_tl, load_gpr(dc, instr.b)) +gen_r_math_logic(nor, nor_tl, load_gpr(dc, instr.b)) + +gen_r_math_logic(srai, sari_tl, instr.imm5) +gen_r_math_logic(srli, shri_tl, instr.imm5) +gen_r_math_logic(slli, shli_tl, instr.imm5) +gen_r_math_logic(roli, rotli_tl, instr.imm5) + +#define gen_r_mul(fname, insn) \ +static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ +{ \ + R_TYPE(instr, (code)); \ + if (likely(instr.c != R_ZERO)) { \ + TCGv t0 = tcg_temp_new(); \ + tcg_gen_##insn(t0, cpu_R[instr.c], \ + load_gpr(dc, instr.a), load_gpr(dc, instr.b)); \ + tcg_temp_free(t0); \ + } \ +} + +gen_r_mul(mulxss, muls2_tl) +gen_r_mul(mulxuu, mulu2_tl) +gen_r_mul(mulxsu, mulsu2_tl) + +#define gen_r_shift_s(fname, insn) \ +static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ +{ \ + R_TYPE(instr, (code)); \ + if (likely(instr.c != R_ZERO)) { \ + TCGv t0 = tcg_temp_new(); \ + tcg_gen_andi_tl(t0, load_gpr((dc), instr.b), 31); \ + tcg_gen_##insn(cpu_R[instr.c], load_gpr((dc), instr.a), t0); \ + tcg_temp_free(t0); \ + } \ +} + +gen_r_shift_s(sra, sar_tl) +gen_r_shift_s(srl, shr_tl) +gen_r_shift_s(sll, shl_tl) +gen_r_shift_s(rol, rotl_tl) +gen_r_shift_s(ror, rotr_tl) + +static void divs(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, (code)); + + /* Stores into R_ZERO are ignored */ + if (unlikely(instr.c == R_ZERO)) { + return; + } + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + + tcg_gen_ext32s_tl(t0, load_gpr(dc, instr.a)); + tcg_gen_ext32s_tl(t1, load_gpr(dc, instr.b)); + tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); + tcg_gen_and_tl(t2, t2, t3); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_tl(t2, t2, t3); + tcg_gen_movi_tl(t3, 0); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_div_tl(cpu_R[instr.c], t0, t1); + tcg_gen_ext32s_tl(cpu_R[instr.c], cpu_R[instr.c]); + + tcg_temp_free(t3); + tcg_temp_free(t2); + tcg_temp_free(t1); + tcg_temp_free(t0); +} + +static void divu(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, (code)); + + /* Stores into R_ZERO are ignored */ + if (unlikely(instr.c == R_ZERO)) { + return; + } + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_const_tl(0); + TCGv t3 = tcg_const_tl(1); + + tcg_gen_ext32u_tl(t0, load_gpr(dc, instr.a)); + tcg_gen_ext32u_tl(t1, load_gpr(dc, instr.b)); + tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_divu_tl(cpu_R[instr.c], t0, t1); + tcg_gen_ext32s_tl(cpu_R[instr.c], cpu_R[instr.c]); + + tcg_temp_free(t3); + tcg_temp_free(t2); + tcg_temp_free(t1); + tcg_temp_free(t0); +} + +static const Nios2Instruction r_type_instructions[] = { + INSTRUCTION_ILLEGAL(), + INSTRUCTION(eret), /* eret */ + INSTRUCTION(roli), /* roli */ + INSTRUCTION(rol), /* rol */ + INSTRUCTION_NOP(), /* flushp */ + INSTRUCTION(ret), /* ret */ + INSTRUCTION(nor), /* nor */ + INSTRUCTION(mulxuu), /* mulxuu */ + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GE), /* cmpge */ + INSTRUCTION(bret), /* bret */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION(ror), /* ror */ + INSTRUCTION_NOP(), /* flushi */ + INSTRUCTION(jmp), /* jmp */ + INSTRUCTION(and), /* and */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LT), /* cmplt */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION(slli), /* slli */ + INSTRUCTION(sll), /* sll */ + INSTRUCTION_UNIMPLEMENTED(), /* wrprs */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION(or), /* or */ + INSTRUCTION(mulxsu), /* mulxsu */ + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_NE), /* cmpne */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION(srli), /* srli */ + INSTRUCTION(srl), /* srl */ + INSTRUCTION(nextpc), /* nextpc */ + INSTRUCTION(callr), /* callr */ + INSTRUCTION(xor), /* xor */ + INSTRUCTION(mulxss), /* mulxss */ + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_EQ), /* cmpeq */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION(divu), /* divu */ + INSTRUCTION(divs), /* div */ + INSTRUCTION(rdctl), /* rdctl */ + INSTRUCTION(mul), /* mul */ + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GEU), /* cmpgeu */ + INSTRUCTION_NOP(), /* initi */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_excp, EXCP_TRAP), /* trap */ + INSTRUCTION(wrctl), /* wrctl */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LTU), /* cmpltu */ + INSTRUCTION(add), /* add */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_excp, EXCP_BREAK), /* break */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION(nop), /* nop */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION(sub), /* sub */ + INSTRUCTION(srai), /* srai */ + INSTRUCTION(sra), /* sra */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), +}; + +static void handle_r_type_instr(DisasContext *dc, uint32_t code, uint32_t flags) +{ + uint8_t opx; + const Nios2Instruction *instr; + + opx = get_opxcode(code); + if (unlikely(opx >= ARRAY_SIZE(r_type_instructions))) { + goto illegal_op; + } + + instr = &r_type_instructions[opx]; + instr->handler(dc, code, instr->flags); + + return; + +illegal_op: + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); +} + +static const char * const regnames[] = { + "zero", "at", "r2", "r3", + "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", + "r20", "r21", "r22", "r23", + "et", "bt", "gp", "sp", + "fp", "ea", "ba", "ra", + "status", "estatus", "bstatus", "ienable", + "ipending", "cpuid", "reserved0", "exception", + "pteaddr", "tlbacc", "tlbmisc", "reserved1", + "badaddr", "config", "mpubase", "mpuacc", + "reserved2", "reserved3", "reserved4", "reserved5", + "reserved6", "reserved7", "reserved8", "reserved9", + "reserved10", "reserved11", "reserved12", "reserved13", + "reserved14", "reserved15", "reserved16", "reserved17", + "rpc" +}; + +#include "exec/gen-icount.h" + +/* generate intermediate code for basic block 'tb'. */ +static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUNios2State *env = cs->env_ptr; + int page_insns; + + dc->mem_idx = cpu_mmu_index(env, false); + + /* Bound the number of insns to execute to those left on the page. */ + page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + dc->base.max_insns = MIN(page_insns, dc->base.max_insns); +} + +static void nios2_tr_tb_start(DisasContextBase *db, CPUState *cs) +{ +} + +static void nios2_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) +{ + tcg_gen_insn_start(dcbase->pc_next); +} + +static void nios2_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUNios2State *env = cs->env_ptr; + const Nios2Instruction *instr; + uint32_t code, pc; + uint8_t op; + + pc = dc->base.pc_next; + dc->pc = pc; + dc->base.pc_next = pc + 4; + + /* Decode an instruction */ + +#if defined(CONFIG_USER_ONLY) + /* FIXME: Is this needed ? */ + if (pc >= 0x1000 && pc < 0x2000) { + t_gen_helper_raise_exception(dc, 0xaa); + return; + } +#endif + + code = cpu_ldl_code(env, pc); + op = get_opcode(code); + + if (unlikely(op >= ARRAY_SIZE(i_type_instructions))) { + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); + return; + } + + dc->zero = NULL; + + instr = &i_type_instructions[op]; + instr->handler(dc, code, instr->flags); + + if (dc->zero) { + tcg_temp_free(dc->zero); + } +} + +static void nios2_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + /* Indicate where the next block should start */ + switch (dc->base.is_jmp) { + case DISAS_TOO_MANY: + case DISAS_UPDATE: + /* Save the current PC back into the CPU register */ + tcg_gen_movi_tl(cpu_R[R_PC], dc->base.pc_next); + tcg_gen_exit_tb(NULL, 0); + break; + + case DISAS_JUMP: + /* The jump will already have updated the PC register */ + tcg_gen_exit_tb(NULL, 0); + break; + + case DISAS_NORETURN: + /* nothing more to generate */ + break; + + default: + g_assert_not_reached(); + } +} + +static void nios2_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) +{ + qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); + log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); +} + +static const TranslatorOps nios2_tr_ops = { + .init_disas_context = nios2_tr_init_disas_context, + .tb_start = nios2_tr_tb_start, + .insn_start = nios2_tr_insn_start, + .translate_insn = nios2_tr_translate_insn, + .tb_stop = nios2_tr_tb_stop, + .disas_log = nios2_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +{ + DisasContext dc; + translator_loop(&nios2_tr_ops, &dc.base, cs, tb, max_insns); +} + +void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags) +{ + Nios2CPU *cpu = NIOS2_CPU(cs); + CPUNios2State *env = &cpu->env; + int i; + + if (!env) { + return; + } + + qemu_fprintf(f, "IN: PC=%x %s\n", + env->regs[R_PC], lookup_symbol(env->regs[R_PC])); + + for (i = 0; i < NUM_CORE_REGS; i++) { + qemu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]); + if ((i + 1) % 4 == 0) { + qemu_fprintf(f, "\n"); + } + } +#if !defined(CONFIG_USER_ONLY) + qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", + env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK, + (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4, + env->mmu.tlbacc_wr); +#endif + qemu_fprintf(f, "\n\n"); +} + +void nios2_tcg_init(void) +{ + int i; + + for (i = 0; i < NUM_CORE_REGS; i++) { + cpu_R[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUNios2State, regs[i]), + regnames[i]); + } +} + +void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, + target_ulong *data) +{ + env->regs[R_PC] = data[0]; +} |