blob: f0bbf78a32e98fdcbfc8cefb5025b83641cf427b [file] [log] [blame]
/*
* 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-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"
/* 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 DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */
#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 {
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 (!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(tb, n);
} else {
tcg_gen_movi_tl(dc->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->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.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->pc + 4 + (instr.imm16.s & -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.imm16.s & -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.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(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.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(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;
}
dc->zero = NULL;
instr = &i_type_instructions[op];
instr->handler(dc, code, instr->flags);
if (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 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(CPUState *cs, TranslationBlock *tb)
{
CPUNios2State *env = cs->env_ptr;
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(tb) & 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(tb) & 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(tb) & 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(NULL, 0);
break;
default:
case DISAS_JUMP:
case DISAS_UPDATE:
/* The jump will already have updated the PC register */
tcg_gen_exit_tb(NULL, 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);
qemu_log("\n");
qemu_log_unlock();
}
#endif
}
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];
}