aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoredgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>2008-05-02 22:16:17 +0000
committeredgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>2008-05-02 22:16:17 +0000
commitb41f7df0189dbda34be3944a48db3b98348e4bc6 (patch)
treeb6c2840eabbfce1f272c47e754686af9e9473403 /target-cris/op_helper.c
parentCRIS: Make CCS related tests pass in system simulation by masking off flags n... (diff)
downloadqemu-kvm-b41f7df0189dbda34be3944a48db3b98348e4bc6.tar.gz
qemu-kvm-b41f7df0189dbda34be3944a48db3b98348e4bc6.tar.bz2
qemu-kvm-b41f7df0189dbda34be3944a48db3b98348e4bc6.zip
CRIS updates:
* Support both the I and D MMUs and improve the accuracy of the MMU model. * Handle the automatic user/kernel stack pointer switching when leaving or entering user mode. * Move the CCS evaluation into helper funcs. * Make sure user-mode cannot change flags only writeable in kernel mode. * More conversion of the translator into TCG. * Handle exceptions while in a delayslot. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4299 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'target-cris/op_helper.c')
-rw-r--r--target-cris/op_helper.c369
1 files changed, 368 insertions, 1 deletions
diff --git a/target-cris/op_helper.c b/target-cris/op_helper.c
index 701c835be..7c629c755 100644
--- a/target-cris/op_helper.c
+++ b/target-cris/op_helper.c
@@ -59,6 +59,9 @@ void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
generated code */
saved_env = env;
env = cpu_single_env;
+
+ D(fprintf(logfile, "%s ra=%x acr=%x %x\n", __func__, retaddr,
+ env->regs[R_ACR], saved_env->regs[R_ACR]));
ret = cpu_cris_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
if (__builtin_expect(ret, 0)) {
if (retaddr) {
@@ -80,16 +83,380 @@ void helper_tlb_update(uint32_t T0)
{
#if !defined(CONFIG_USER_ONLY)
uint32_t vaddr;
+ uint32_t srs = env->pregs[PR_SRS];
+
+ if (srs != 1 && srs != 2)
+ return;
vaddr = cris_mmu_tlb_latest_update(env, T0);
- D(printf("flush vaddr %x\n", vaddr));
+ D(printf("flush old_vaddr=%x vaddr=%x T0=%x\n", vaddr,
+ env->sregs[SFR_R_MM_CAUSE] & TARGET_PAGE_MASK, T0));
tlb_flush_page(env, vaddr);
#endif
}
+void helper_tlb_flush(void)
+{
+ tlb_flush(env, 1);
+}
+
+void helper_dump(uint32_t a0, uint32_t a1)
+{
+ (fprintf(logfile, "%s: a0=%x a1=%x\n", __func__, a0, a1));
+}
+
+void helper_dummy(void)
+{
+
+}
+
+/* Only used for debugging at the moment. */
+void helper_rfe(void)
+{
+ D(fprintf(logfile, "rfe: erp=%x pid=%x ccs=%x btarget=%x\n",
+ env->pregs[PR_ERP], env->pregs[PR_PID],
+ env->pregs[PR_CCS],
+ env->btarget));
+}
+
+void helper_store(uint32_t a0)
+{
+ if (env->pregs[PR_CCS] & P_FLAG )
+ {
+ cpu_abort(env, "cond_store_failed! pc=%x a0=%x\n",
+ env->pc, a0);
+ }
+}
+
void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
int is_asi)
{
D(printf("%s addr=%x w=%d ex=%d asi=%d\n",
__func__, addr, is_write, is_exec, is_asi));
}
+
+static void evaluate_flags_writeback(uint32_t flags)
+{
+ int x;
+
+ /* Extended arithmetics, leave the z flag alone. */
+ env->debug3 = env->pregs[PR_CCS];
+
+ if (env->cc_x_live)
+ x = env->cc_x;
+ else
+ x = env->pregs[PR_CCS] & X_FLAG;
+
+ if ((x || env->cc_op == CC_OP_ADDC)
+ && flags & Z_FLAG)
+ env->cc_mask &= ~Z_FLAG;
+
+ /* all insn clear the x-flag except setf or clrf. */
+ env->pregs[PR_CCS] &= ~(env->cc_mask | X_FLAG);
+ flags &= env->cc_mask;
+ env->pregs[PR_CCS] |= flags;
+ RETURN();
+}
+
+void helper_evaluate_flags_muls(void)
+{
+ uint32_t src;
+ uint32_t dst;
+ uint32_t res;
+ uint32_t flags = 0;
+ /* were gonna have to redo the muls. */
+ int64_t tmp, t0 ,t1;
+ int32_t mof;
+ int dneg;
+
+ src = env->cc_src;
+ dst = env->cc_dest;
+ res = env->cc_result;
+
+
+ /* cast into signed values to make GCC sign extend. */
+ t0 = (int32_t)src;
+ t1 = (int32_t)dst;
+ dneg = ((int32_t)res) < 0;
+
+ tmp = t0 * t1;
+ mof = tmp >> 32;
+ if (tmp == 0)
+ flags |= Z_FLAG;
+ else if (tmp < 0)
+ flags |= N_FLAG;
+ if ((dneg && mof != -1)
+ || (!dneg && mof != 0))
+ flags |= V_FLAG;
+ evaluate_flags_writeback(flags);
+}
+
+void helper_evaluate_flags_mulu(void)
+{
+ uint32_t src;
+ uint32_t dst;
+ uint32_t res;
+ uint32_t flags = 0;
+ /* were gonna have to redo the muls. */
+ uint64_t tmp, t0 ,t1;
+ uint32_t mof;
+
+ src = env->cc_src;
+ dst = env->cc_dest;
+ res = env->cc_result;
+
+
+ /* cast into signed values to make GCC sign extend. */
+ t0 = src;
+ t1 = dst;
+
+ tmp = t0 * t1;
+ mof = tmp >> 32;
+ if (tmp == 0)
+ flags |= Z_FLAG;
+ else if (tmp >> 63)
+ flags |= N_FLAG;
+ if (mof)
+ flags |= V_FLAG;
+
+ evaluate_flags_writeback(flags);
+}
+
+void helper_evaluate_flags_mcp(void)
+{
+ uint32_t src;
+ uint32_t dst;
+ uint32_t res;
+ uint32_t flags = 0;
+
+ src = env->cc_src;
+ dst = env->cc_dest;
+ res = env->cc_result;
+
+ if ((res & 0x80000000L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (((src & 0x80000000L) == 0L)
+ && ((dst & 0x80000000L) == 0L))
+ {
+ flags |= V_FLAG;
+ }
+ else if (((src & 0x80000000L) != 0L) &&
+ ((dst & 0x80000000L) != 0L))
+ {
+ flags |= R_FLAG;
+ }
+ }
+ else
+ {
+ if (res == 0L)
+ flags |= Z_FLAG;
+ if (((src & 0x80000000L) != 0L)
+ && ((dst & 0x80000000L) != 0L))
+ flags |= V_FLAG;
+ if ((dst & 0x80000000L) != 0L
+ || (src & 0x80000000L) != 0L)
+ flags |= R_FLAG;
+ }
+
+ evaluate_flags_writeback(flags);
+}
+
+void helper_evaluate_flags_alu_4(void)
+{
+ uint32_t src;
+ uint32_t dst;
+ uint32_t res;
+ uint32_t flags = 0;
+
+ src = env->cc_src;
+ dst = env->cc_dest;
+ res = env->cc_result;
+
+ if ((res & 0x80000000L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (((src & 0x80000000L) == 0L)
+ && ((dst & 0x80000000L) == 0L))
+ {
+ flags |= V_FLAG;
+ }
+ else if (((src & 0x80000000L) != 0L) &&
+ ((dst & 0x80000000L) != 0L))
+ {
+ flags |= C_FLAG;
+ }
+ }
+ else
+ {
+ if (res == 0L)
+ flags |= Z_FLAG;
+ if (((src & 0x80000000L) != 0L)
+ && ((dst & 0x80000000L) != 0L))
+ flags |= V_FLAG;
+ if ((dst & 0x80000000L) != 0L
+ || (src & 0x80000000L) != 0L)
+ flags |= C_FLAG;
+ }
+
+ if (env->cc_op == CC_OP_SUB
+ || env->cc_op == CC_OP_CMP) {
+ flags ^= C_FLAG;
+ }
+ evaluate_flags_writeback(flags);
+}
+
+void helper_evaluate_flags_move_4 (void)
+{
+ uint32_t src;
+ uint32_t res;
+ uint32_t flags = 0;
+
+ src = env->cc_src;
+ res = env->cc_result;
+
+ if ((int32_t)res < 0)
+ flags |= N_FLAG;
+ else if (res == 0L)
+ flags |= Z_FLAG;
+
+ evaluate_flags_writeback(flags);
+}
+void helper_evaluate_flags_move_2 (void)
+{
+ uint32_t src;
+ uint32_t flags = 0;
+ uint16_t res;
+
+ src = env->cc_src;
+ res = env->cc_result;
+
+ if ((int16_t)res < 0L)
+ flags |= N_FLAG;
+ else if (res == 0)
+ flags |= Z_FLAG;
+
+ evaluate_flags_writeback(flags);
+}
+
+/* TODO: This is expensive. We could split things up and only evaluate part of
+ CCR on a need to know basis. For now, we simply re-evaluate everything. */
+void helper_evaluate_flags (void)
+{
+ uint32_t src;
+ uint32_t dst;
+ uint32_t res;
+ uint32_t flags = 0;
+
+ src = env->cc_src;
+ dst = env->cc_dest;
+ res = env->cc_result;
+
+
+ /* Now, evaluate the flags. This stuff is based on
+ Per Zander's CRISv10 simulator. */
+ switch (env->cc_size)
+ {
+ case 1:
+ if ((res & 0x80L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (((src & 0x80L) == 0L)
+ && ((dst & 0x80L) == 0L))
+ {
+ flags |= V_FLAG;
+ }
+ else if (((src & 0x80L) != 0L)
+ && ((dst & 0x80L) != 0L))
+ {
+ flags |= C_FLAG;
+ }
+ }
+ else
+ {
+ if ((res & 0xFFL) == 0L)
+ {
+ flags |= Z_FLAG;
+ }
+ if (((src & 0x80L) != 0L)
+ && ((dst & 0x80L) != 0L))
+ {
+ flags |= V_FLAG;
+ }
+ if ((dst & 0x80L) != 0L
+ || (src & 0x80L) != 0L)
+ {
+ flags |= C_FLAG;
+ }
+ }
+ break;
+ case 2:
+ if ((res & 0x8000L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (((src & 0x8000L) == 0L)
+ && ((dst & 0x8000L) == 0L))
+ {
+ flags |= V_FLAG;
+ }
+ else if (((src & 0x8000L) != 0L)
+ && ((dst & 0x8000L) != 0L))
+ {
+ flags |= C_FLAG;
+ }
+ }
+ else
+ {
+ if ((res & 0xFFFFL) == 0L)
+ {
+ flags |= Z_FLAG;
+ }
+ if (((src & 0x8000L) != 0L)
+ && ((dst & 0x8000L) != 0L))
+ {
+ flags |= V_FLAG;
+ }
+ if ((dst & 0x8000L) != 0L
+ || (src & 0x8000L) != 0L)
+ {
+ flags |= C_FLAG;
+ }
+ }
+ break;
+ case 4:
+ if ((res & 0x80000000L) != 0L)
+ {
+ flags |= N_FLAG;
+ if (((src & 0x80000000L) == 0L)
+ && ((dst & 0x80000000L) == 0L))
+ {
+ flags |= V_FLAG;
+ }
+ else if (((src & 0x80000000L) != 0L) &&
+ ((dst & 0x80000000L) != 0L))
+ {
+ flags |= C_FLAG;
+ }
+ }
+ else
+ {
+ if (res == 0L)
+ flags |= Z_FLAG;
+ if (((src & 0x80000000L) != 0L)
+ && ((dst & 0x80000000L) != 0L))
+ flags |= V_FLAG;
+ if ((dst & 0x80000000L) != 0L
+ || (src & 0x80000000L) != 0L)
+ flags |= C_FLAG;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (env->cc_op == CC_OP_SUB
+ || env->cc_op == CC_OP_CMP) {
+ flags ^= C_FLAG;
+ }
+ evaluate_flags_writeback(flags);
+}