@@ -220,6 +220,7 @@ struct ArchCPU {
bool diverr_present;
bool mmu_present;
+ bool eic_present;
uint32_t pid_num_bits;
uint32_t tlb_num_ways;
@@ -24,6 +24,8 @@ DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, i32, env, i32, i32)
#if !defined(CONFIG_USER_ONLY)
DEF_HELPER_3(eret, noreturn, env, i32, i32)
+DEF_HELPER_FLAGS_2(rdprs, TCG_CALL_NO_WG, i32, env, i32)
+DEF_HELPER_3(wrprs, void, env, i32, i32)
DEF_HELPER_2(mmu_write_tlbacc, void, env, i32)
DEF_HELPER_2(mmu_write_tlbmisc, void, env, i32)
DEF_HELPER_2(mmu_write_pteaddr, void, env, i32)
@@ -83,4 +83,20 @@ void helper_eret(CPUNios2State *env, uint32_t new_status, uint32_t new_pc)
env->pc = new_pc;
cpu_loop_exit(cs);
}
+
+/*
+ * RDPRS and WRPRS are implemented out of line so that if PRS == CRS,
+ * all of the tcg global temporaries are synced back to ENV.
+ */
+uint32_t helper_rdprs(CPUNios2State *env, uint32_t regno)
+{
+ unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS);
+ return env->shadow_regs[prs][regno];
+}
+
+void helper_wrprs(CPUNios2State *env, uint32_t regno, uint32_t val)
+{
+ unsigned prs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, PRS);
+ env->shadow_regs[prs][regno] = val;
+}
#endif /* !CONFIG_USER_ONLY */
@@ -130,6 +130,7 @@ typedef struct DisasContext {
uint32_t tb_flags;
TCGv sink;
const ControlRegState *cr_state;
+ bool eic_present;
} DisasContext;
static TCGv cpu_R[NUM_GP_REGS];
@@ -387,6 +388,27 @@ gen_i_math_logic(andhi, andi, 0, imm_shifted)
gen_i_math_logic(orhi , ori, 1, imm_shifted)
gen_i_math_logic(xorhi, xori, 1, imm_shifted)
+/* rB <- prs.rA + sigma(IMM16) */
+static void rdprs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ if (!dc->eic_present) {
+ t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+ return;
+ }
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
+
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ I_TYPE(instr, code);
+ TCGv dest = dest_gpr(dc, instr.b);
+ gen_helper_rdprs(dest, cpu_env, tcg_constant_i32(instr.a));
+ tcg_gen_addi_tl(dest, dest, instr.imm16.s);
+#endif
+}
+
/* Prototype only, defined below */
static void handle_r_type_instr(DisasContext *dc, uint32_t code,
uint32_t flags);
@@ -448,7 +470,7 @@ static const Nios2Instruction i_type_instructions[] = {
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(rdprs), /* rdprs */
INSTRUCTION_ILLEGAL(),
INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */
INSTRUCTION_NOP(), /* flushd */
@@ -648,6 +670,36 @@ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags)
#endif
}
+/* prs.rC <- rA */
+static void wrprs(DisasContext *dc, uint32_t code, uint32_t flags)
+{
+ if (!dc->eic_present) {
+ t_gen_helper_raise_exception(dc, EXCP_ILLEGAL);
+ return;
+ }
+ if (!gen_check_supervisor(dc)) {
+ return;
+ }
+
+#ifdef CONFIG_USER_ONLY
+ g_assert_not_reached();
+#else
+ R_TYPE(instr, code);
+ gen_helper_wrprs(cpu_env, tcg_constant_i32(instr.c),
+ load_gpr(dc, instr.a));
+ /*
+ * The expected write to PRS[r0] is 0, from CRS[r0].
+ * If not, and CRS == PRS (which we cannot tell from here),
+ * we may now have a non-zero value in our current r0.
+ * By ending the TB, we re-evaluate tb_flags and find out.
+ */
+ if (instr.c == 0
+ && (instr.a != 0 || !FIELD_EX32(dc->tb_flags, TBFLAGS, R0_0))) {
+ dc->base.is_jmp = DISAS_UPDATE;
+ }
+#endif
+}
+
/* Comparison instructions */
static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags)
{
@@ -793,7 +845,7 @@ static const Nios2Instruction r_type_instructions[] = {
INSTRUCTION_ILLEGAL(),
INSTRUCTION(slli), /* slli */
INSTRUCTION(sll), /* sll */
- INSTRUCTION_UNIMPLEMENTED(), /* wrprs */
+ INSTRUCTION(wrprs), /* wrprs */
INSTRUCTION_ILLEGAL(),
INSTRUCTION(or), /* or */
INSTRUCTION(mulxsu), /* mulxsu */
@@ -895,6 +947,7 @@ static void nios2_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
dc->mem_idx = cpu_mmu_index(env, false);
dc->cr_state = cpu->cr_state;
dc->tb_flags = dc->base.tb->flags;
+ dc->eic_present = cpu->eic_present;
/* Bound the number of insns to execute to those left on the page. */
page_insns = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4;