summaryrefslogtreecommitdiff
path: root/target/nios2/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/nios2/translate.c')
-rw-r--r--target/nios2/translate.c958
1 files changed, 958 insertions, 0 deletions
diff --git a/target/nios2/translate.c b/target/nios2/translate.c
new file mode 100644
index 0000000000..2d738391ad
--- /dev/null
+++ b/target/nios2/translate.c
@@ -0,0 +1,958 @@
+/*
+ * 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 "cpu.h"
+#include "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"
+
+#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 imm16; \
+ int16_t imm16s; \
+ }; \
+ uint8_t b; \
+ uint8_t a; \
+ } (instr) = { \
+ .op = extract32((code), 0, 6), \
+ .imm16 = 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 {
+ TCGv_ptr cpu_env;
+ TCGv *cpu_R;
+ TCGv_i32 zero;
+ int is_jmp;
+ target_ulong pc;
+ TranslationBlock *tb;
+ int mem_idx;
+ bool singlestep_enabled;
+} DisasContext;
+
+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 (TCGV_IS_UNUSED_I32(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 dc->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(dc->cpu_R[R_PC], dc->pc);
+ gen_helper_raise_exception(dc->cpu_env, tmp);
+ tcg_temp_free_i32(tmp);
+ dc->is_jmp = DISAS_UPDATE;
+}
+
+static bool use_goto_tb(DisasContext *dc, uint32_t dest)
+{
+ if (unlikely(dc->singlestep_enabled)) {
+ return false;
+ }
+
+#ifndef CONFIG_USER_ONLY
+ return (dc->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK);
+#else
+ return true;
+#endif
+}
+
+static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest)
+{
+ TranslationBlock *tb = dc->tb;
+
+ if (use_goto_tb(dc, dest)) {
+ tcg_gen_goto_tb(n);
+ tcg_gen_movi_tl(dc->cpu_R[R_PC], dest);
+ tcg_gen_exit_tb((tcg_target_long)tb + n);
+ } else {
+ tcg_gen_movi_tl(dc->cpu_R[R_PC], dest);
+ tcg_gen_exit_tb(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->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->is_jmp = DISAS_TB_JUMP;
+}
+
+static void call(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
+ 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 = dc->cpu_R[instr.b];
+ } else {
+ data = tcg_temp_new();
+ }
+
+ tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16s);
+ 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.imm16s);
+ 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->pc + 4 + (instr.imm16s & -4));
+ dc->is_jmp = DISAS_TB_JUMP;
+}
+
+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, dc->cpu_R[instr.a], dc->cpu_R[instr.b], l1);
+ gen_goto_tb(dc, 0, dc->pc + 4);
+ gen_set_label(l1);
+ gen_goto_tb(dc, 1, dc->pc + 4 + (instr.imm16s & -4));
+ dc->is_jmp = DISAS_TB_JUMP;
+}
+
+/* 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, (dc)->cpu_R[instr.b], (dc)->cpu_R[instr.a], \
+ (op3)); \
+}
+
+gen_i_cmpxx(gen_cmpxxsi, instr.imm16s)
+gen_i_cmpxx(gen_cmpxxui, instr.imm16)
+
+/* 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(dc->cpu_R[instr.b], (resimm) ? (op3) : 0); \
+ } else { \
+ tcg_gen_##insn##_tl((dc)->cpu_R[instr.b], (dc)->cpu_R[instr.a], \
+ (op3)); \
+ } \
+}
+
+gen_i_math_logic(addi, addi, 1, instr.imm16s)
+gen_i_math_logic(muli, muli, 0, instr.imm16s)
+
+gen_i_math_logic(andi, andi, 0, instr.imm16)
+gen_i_math_logic(ori, ori, 1, instr.imm16)
+gen_i_math_logic(xori, xori, 1, instr.imm16)
+
+gen_i_math_logic(andhi, andi, 0, instr.imm16 << 16)
+gen_i_math_logic(orhi , ori, 1, instr.imm16 << 16)
+gen_i_math_logic(xorhi, xori, 1, instr.imm16 << 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(dc->cpu_R[CR_STATUS], dc->cpu_R[CR_ESTATUS]);
+ tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_EA]);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* PC <- ra */
+static void ret(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_RA]);
+
+ dc->is_jmp = DISAS_JUMP;
+}
+
+/* PC <- ba */
+static void bret(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_BA]);
+
+ dc->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(dc->cpu_R[R_PC], load_gpr(dc, instr.a));
+
+ dc->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(dc->cpu_R[instr.c], dc->pc + 4);
+ }
+}
+
+/*
+ * ra <- PC + 4
+ * PC <- rA
+ */
+static void callr(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ R_TYPE(instr, code);
+
+ tcg_gen_mov_tl(dc->cpu_R[R_PC], load_gpr(dc, instr.a));
+ tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4);
+
+ dc->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(dc->cpu_R[instr.c], dc->cpu_R[instr.imm5 + CR_BASE]);
+#ifdef DEBUG_MMU
+ TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE);
+ gen_helper_mmu_read_debug(dc->cpu_R[instr.c], dc->cpu_env, tmp);
+ tcg_temp_free_i32(tmp);
+#endif
+ }
+#endif
+ break;
+ }
+
+ default:
+ if (likely(instr.c != R_ZERO)) {
+ tcg_gen_mov_tl(dc->cpu_R[instr.c], dc->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(dc->cpu_env, tmp, load_gpr(dc, instr.a));
+ tcg_temp_free_i32(tmp);
+#endif
+ break;
+ }
+
+ default:
+ tcg_gen_mov_tl(dc->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) {
+ gen_helper_check_interrupts(dc->cpu_env);
+ }
+#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, dc->cpu_R[instr.c], dc->cpu_R[instr.a],
+ dc->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((dc)->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, dc->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((dc)->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(dc->cpu_R[instr.c], t0, t1);
+ tcg_gen_ext32s_tl(dc->cpu_R[instr.c], dc->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(dc->cpu_R[instr.c], t0, t1);
+ tcg_gen_ext32s_tl(dc->cpu_R[instr.c], dc->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 void handle_instruction(DisasContext *dc, CPUNios2State *env)
+{
+ uint32_t code;
+ uint8_t op;
+ const Nios2Instruction *instr;
+#if defined(CONFIG_USER_ONLY)
+ /* FIXME: Is this needed ? */
+ if (dc->pc >= 0x1000 && dc->pc < 0x2000) {
+ env->regs[R_PC] = dc->pc;
+ t_gen_helper_raise_exception(dc, 0xaa);
+ return;
+ }
+#endif
+ code = cpu_ldl_code(env, dc->pc);
+ op = get_opcode(code);
+
+ if (unlikely(op >= ARRAY_SIZE(i_type_instructions))) {
+ goto illegal_op;
+ }
+
+ TCGV_UNUSED_I32(dc->zero);
+
+ instr = &i_type_instructions[op];
+ instr->handler(dc, code, instr->flags);
+
+ if (!TCGV_IS_UNUSED_I32(dc->zero)) {
+ tcg_temp_free(dc->zero);
+ }
+
+ 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"
+};
+
+static TCGv_ptr cpu_env;
+static TCGv cpu_R[NUM_CORE_REGS];
+
+#include "exec/gen-icount.h"
+
+static void gen_exception(DisasContext *dc, uint32_t excp)
+{
+ TCGv_i32 tmp = tcg_const_i32(excp);
+
+ tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
+ gen_helper_raise_exception(cpu_env, tmp);
+ tcg_temp_free_i32(tmp);
+ dc->is_jmp = DISAS_UPDATE;
+}
+
+/* generate intermediate code for basic block 'tb'. */
+void gen_intermediate_code(CPUNios2State *env, TranslationBlock *tb)
+{
+ Nios2CPU *cpu = nios2_env_get_cpu(env);
+ CPUState *cs = CPU(cpu);
+ DisasContext dc1, *dc = &dc1;
+ int num_insns;
+ int max_insns;
+
+ /* Initialize DC */
+ dc->cpu_env = cpu_env;
+ dc->cpu_R = cpu_R;
+ dc->is_jmp = DISAS_NEXT;
+ dc->pc = tb->pc;
+ dc->tb = tb;
+ dc->mem_idx = cpu_mmu_index(env, false);
+ dc->singlestep_enabled = cs->singlestep_enabled;
+
+ /* Set up instruction counts */
+ num_insns = 0;
+ if (cs->singlestep_enabled || singlestep) {
+ max_insns = 1;
+ } else {
+ int page_insns = (TARGET_PAGE_SIZE - (tb->pc & TARGET_PAGE_MASK)) / 4;
+ max_insns = tb->cflags & CF_COUNT_MASK;
+ if (max_insns == 0) {
+ max_insns = CF_COUNT_MASK;
+ }
+ if (max_insns > page_insns) {
+ max_insns = page_insns;
+ }
+ if (max_insns > TCG_MAX_INSNS) {
+ max_insns = TCG_MAX_INSNS;
+ }
+ }
+
+ gen_tb_start(tb);
+ do {
+ tcg_gen_insn_start(dc->pc);
+ num_insns++;
+
+ if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) {
+ gen_exception(dc, EXCP_DEBUG);
+ /* The address covered by the breakpoint must be included in
+ [tb->pc, tb->pc + tb->size) in order to for it to be
+ properly cleared -- thus we increment the PC here so that
+ the logic setting tb->size below does the right thing. */
+ dc->pc += 4;
+ break;
+ }
+
+ if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) {
+ gen_io_start();
+ }
+
+ /* Decode an instruction */
+ handle_instruction(dc, env);
+
+ dc->pc += 4;
+
+ /* 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. */
+ } while (!dc->is_jmp &&
+ !tcg_op_buf_full() &&
+ num_insns < max_insns);
+
+ if (tb->cflags & CF_LAST_IO) {
+ gen_io_end();
+ }
+
+ /* Indicate where the next block should start */
+ switch (dc->is_jmp) {
+ case DISAS_NEXT:
+ /* Save the current PC back into the CPU register */
+ tcg_gen_movi_tl(cpu_R[R_PC], dc->pc);
+ tcg_gen_exit_tb(0);
+ break;
+
+ default:
+ case DISAS_JUMP:
+ case DISAS_UPDATE:
+ /* The jump will already have updated the PC register */
+ tcg_gen_exit_tb(0);
+ break;
+
+ case DISAS_TB_JUMP:
+ /* nothing more to generate */
+ break;
+ }
+
+ /* End off the block */
+ gen_tb_end(tb, num_insns);
+
+ /* Mark instruction starts for the final generated instruction */
+ tb->size = dc->pc - tb->pc;
+ tb->icount = num_insns;
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
+ && qemu_log_in_addr_range(tb->pc)) {
+ qemu_log_lock();
+ qemu_log("IN: %s\n", lookup_symbol(tb->pc));
+ log_target_disas(cs, tb->pc, dc->pc - tb->pc, 0);
+ qemu_log("\n");
+ qemu_log_unlock();
+ }
+#endif
+}
+
+void nios2_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
+ int flags)
+{
+ Nios2CPU *cpu = NIOS2_CPU(cs);
+ CPUNios2State *env = &cpu->env;
+ int i;
+
+ if (!env || !f) {
+ return;
+ }
+
+ cpu_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++) {
+ cpu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]);
+ if ((i + 1) % 4 == 0) {
+ cpu_fprintf(f, "\n");
+ }
+ }
+#if !defined(CONFIG_USER_ONLY)
+ cpu_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
+ cpu_fprintf(f, "\n\n");
+}
+
+void nios2_tcg_init(void)
+{
+ int i;
+
+ cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
+
+ 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];
+}