summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2003-07-09 17:10:32 +0000
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2003-07-09 17:10:32 +0000
commit6e295807acbaf7eb3200a685376fb968ebdb8571 (patch)
treee5292b167e9550bf6997fd4372108393d0e9454b
parentf2674e31e0b79a8e30335438f274b846d084a383 (diff)
downloadqemu-6e295807acbaf7eb3200a685376fb968ebdb8571.tar.gz
ARM fixes
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@314 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--arm-dis.c4
-rw-r--r--exec-arm.h8
-rw-r--r--op-arm.c31
-rw-r--r--translate-arm.c224
4 files changed, 209 insertions, 58 deletions
diff --git a/arm-dis.c b/arm-dis.c
index adacd898b6..a84d91b488 100644
--- a/arm-dis.c
+++ b/arm-dis.c
@@ -560,8 +560,8 @@ static arm_regname regnames[] =
{ "a1", "a2", "a3", "a4", "v1", "v2", "v3", "WR", "v5", "SB", "SL", "FP", "IP", "SP", "LR", "PC" }}
};
-/* Default to GCC register name set. */
-static unsigned int regname_selected = 1;
+/* Default to STD register name set. */
+static unsigned int regname_selected = 2;
#define NUM_ARM_REGNAMES NUM_ELEM (regnames)
#define arm_regnames regnames[regname_selected].reg_names
diff --git a/exec-arm.h b/exec-arm.h
index 8da263672b..e838e88239 100644
--- a/exec-arm.h
+++ b/exec-arm.h
@@ -30,3 +30,11 @@ register uint32_t T2 asm(AREG3);
void cpu_lock(void);
void cpu_unlock(void);
void cpu_loop_exit(void);
+
+static inline int compute_cpsr(void)
+{
+ int ZF;
+ ZF = (env->NZF == 0);
+ return env->cpsr | (env->NZF & 0x80000000) | (ZF << 30) |
+ (env->CF << 29) | ((env->VF & 0x80000000) >> 3);
+}
diff --git a/op-arm.c b/op-arm.c
index 40cf3cd253..8e5cce0e9b 100644
--- a/op-arm.c
+++ b/op-arm.c
@@ -154,11 +154,11 @@ void OPPROTO op_adcl_T0_T1_cc(void)
FORCE_RET();
}
-#define OPSUB(sub, sbc, T0, T1) \
+#define OPSUB(sub, sbc, res, T0, T1) \
\
void OPPROTO op_ ## sub ## l_T0_T1(void) \
{ \
- T0 -= T1; \
+ res = T0 - T1; \
} \
\
void OPPROTO op_ ## sub ## l_T0_T1_cc(void) \
@@ -167,13 +167,14 @@ void OPPROTO op_ ## sub ## l_T0_T1_cc(void) \
src1 = T0; \
T0 -= T1; \
env->NZF = T0; \
- env->CF = src1 < T1; \
+ env->CF = src1 >= T1; \
env->VF = (src1 ^ T1) & (src1 ^ T0); \
+ res = T0; \
} \
\
void OPPROTO op_ ## sbc ## l_T0_T1(void) \
{ \
- T0 = T0 - T1 + env->CF - 1; \
+ res = T0 - T1 + env->CF - 1; \
} \
\
void OPPROTO op_ ## sbc ## l_T0_T1_cc(void) \
@@ -182,20 +183,20 @@ void OPPROTO op_ ## sbc ## l_T0_T1_cc(void) \
src1 = T0; \
if (!env->CF) { \
T0 = T0 - T1 - 1; \
- T0 += T1; \
- env->CF = src1 < T1; \
+ env->CF = src1 >= T1; \
} else { \
T0 = T0 - T1; \
- env->CF = src1 <= T1; \
+ env->CF = src1 > T1; \
} \
env->VF = (src1 ^ T1) & (src1 ^ T0); \
env->NZF = T0; \
+ res = T0; \
FORCE_RET(); \
}
-OPSUB(sub, sbc, T0, T1)
+OPSUB(sub, sbc, T0, T0, T1)
-OPSUB(rsb, rsc, T1, T0)
+OPSUB(rsb, rsc, T0, T1, T0)
void OPPROTO op_andl_T0_T1(void)
{
@@ -222,11 +223,16 @@ void OPPROTO op_notl_T1(void)
T1 = ~T1;
}
-void OPPROTO op_logic_cc(void)
+void OPPROTO op_logic_T0_cc(void)
{
env->NZF = T0;
}
+void OPPROTO op_logic_T1_cc(void)
+{
+ env->NZF = T1;
+}
+
#define EIP (env->regs[15])
void OPPROTO op_test_eq(void)
@@ -334,10 +340,7 @@ void OPPROTO op_jmp(void)
void OPPROTO op_movl_T0_psr(void)
{
- int ZF;
- ZF = (env->NZF == 0);
- T0 = env->cpsr | (env->NZF & 0x80000000) | (ZF << 30) |
- (env->CF << 29) | ((env->VF & 0x80000000) >> 3);
+ T0 = compute_cpsr();
}
/* NOTE: N = 1 and Z = 1 cannot be stored currently */
diff --git a/translate-arm.c b/translate-arm.c
index 2413f02a55..6020e772d3 100644
--- a/translate-arm.c
+++ b/translate-arm.c
@@ -34,6 +34,8 @@ typedef struct DisasContext {
struct TranslationBlock *tb;
} DisasContext;
+#define DISAS_JUMP_NEXT 4
+
/* XXX: move that elsewhere */
static uint16_t *gen_opc_ptr;
static uint32_t *gen_opparam_ptr;
@@ -333,10 +335,11 @@ static void disas_arm_insn(DisasContext *s)
/* if not always execute, we generate a conditional jump to
next instruction */
gen_test_cc[cond ^ 1]((long)s->tb, (long)s->pc);
- s->is_jmp = 1;
+ s->is_jmp = DISAS_JUMP_NEXT;
}
- if ((insn & 0x0c000000) == 0 &&
- (insn & 0x00000090) != 0x90) {
+ if (((insn & 0x0e000000) == 0 &&
+ (insn & 0x00000090) != 0x90) ||
+ ((insn & 0x0e000000) == (1 << 25))) {
int set_cc, logic_cc, shiftop;
op1 = (insn >> 21) & 0xf;
@@ -367,7 +370,7 @@ static void disas_arm_insn(DisasContext *s)
}
}
} else {
- rs = (insn >> 16) & 0xf;
+ rs = (insn >> 8) & 0xf;
gen_movl_T0_reg(s, rs);
if (logic_cc) {
gen_shift_T1_T0_cc[shiftop]();
@@ -385,10 +388,14 @@ static void disas_arm_insn(DisasContext *s)
case 0x00:
gen_op_andl_T0_T1();
gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
break;
case 0x01:
gen_op_xorl_T0_T1();
gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
break;
case 0x02:
if (set_cc)
@@ -435,11 +442,13 @@ static void disas_arm_insn(DisasContext *s)
case 0x08:
if (set_cc) {
gen_op_andl_T0_T1();
+ gen_op_logic_T0_cc();
}
break;
case 0x09:
if (set_cc) {
gen_op_xorl_T0_T1();
+ gen_op_logic_T0_cc();
}
break;
case 0x0a:
@@ -455,22 +464,28 @@ static void disas_arm_insn(DisasContext *s)
case 0x0c:
gen_op_orl_T0_T1();
gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
break;
case 0x0d:
gen_movl_reg_T1(s, rd);
+ if (logic_cc)
+ gen_op_logic_T1_cc();
break;
case 0x0e:
gen_op_bicl_T0_T1();
gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
break;
default:
case 0x0f:
gen_op_notl_T1();
gen_movl_reg_T1(s, rd);
+ if (logic_cc)
+ gen_op_logic_T1_cc();
break;
}
- if (logic_cc)
- gen_op_logic_cc();
} else {
/* other instructions */
op1 = (insn >> 24) & 0xf;
@@ -494,7 +509,7 @@ static void disas_arm_insn(DisasContext *s)
gen_op_addl_T0_T1();
}
if (insn & (1 << 20))
- gen_op_logic_cc();
+ gen_op_logic_T0_cc();
gen_movl_reg_T0(s, rd);
} else {
/* 64 bit mul */
@@ -551,10 +566,12 @@ static void disas_arm_insn(DisasContext *s)
/* store */
gen_op_stw_T0_T1();
}
- if (!(insn & (1 << 24)))
+ if (!(insn & (1 << 24))) {
gen_add_datah_offset(s, insn);
- if (insn & (1 << 21))
gen_movl_reg_T1(s, rn);
+ } else if (insn & (1 << 21)) {
+ gen_movl_reg_T1(s, rn);
+ }
}
break;
case 0x4:
@@ -582,40 +599,94 @@ static void disas_arm_insn(DisasContext *s)
else
gen_op_stl_T0_T1();
}
- if (!(insn & (1 << 24)))
+ if (!(insn & (1 << 24))) {
gen_add_data_offset(s, insn);
- if (insn & (1 << 21))
gen_movl_reg_T1(s, rn);
+ } else if (insn & (1 << 21))
+ gen_movl_reg_T1(s, rn); {
+ }
break;
case 0x08:
case 0x09:
- /* load/store multiple words */
- if (insn & (1 << 22))
- goto illegal_op; /* only usable in supervisor mode */
- rn = (insn >> 16) & 0xf;
- gen_movl_T1_reg(s, rn);
- val = 4;
- if (!(insn & (1 << 23)))
- val = -val;
- for(i=0;i<16;i++) {
- if (insn & (1 << i)) {
- if (insn & (1 << 24))
- gen_op_addl_T1_im(val);
- if (insn & (1 << 20)) {
- /* load */
- gen_op_ldl_T0_T1();
- gen_movl_reg_T0(s, i);
+ {
+ int j, n;
+ /* load/store multiple words */
+ /* XXX: store correct base if write back */
+ if (insn & (1 << 22))
+ goto illegal_op; /* only usable in supervisor mode */
+ rn = (insn >> 16) & 0xf;
+ gen_movl_T1_reg(s, rn);
+
+ /* compute total size */
+ n = 0;
+ for(i=0;i<16;i++) {
+ if (insn & (1 << i))
+ n++;
+ }
+ /* XXX: test invalid n == 0 case ? */
+ if (insn & (1 << 23)) {
+ if (insn & (1 << 24)) {
+ /* pre increment */
+ gen_op_addl_T1_im(4);
} else {
- /* store */
- gen_movl_T0_reg(s, i);
- gen_op_stl_T0_T1();
+ /* post increment */
+ }
+ } else {
+ if (insn & (1 << 24)) {
+ /* pre decrement */
+ gen_op_addl_T1_im(-(n * 4));
+ } else {
+ /* post decrement */
+ if (n != 1)
+ gen_op_addl_T1_im(-((n - 1) * 4));
+ }
+ }
+ j = 0;
+ for(i=0;i<16;i++) {
+ if (insn & (1 << i)) {
+ if (insn & (1 << 20)) {
+ /* load */
+ gen_op_ldl_T0_T1();
+ gen_movl_reg_T0(s, i);
+ } else {
+ /* store */
+ if (i == 15) {
+ /* special case: r15 = PC + 12 */
+ val = (long)s->pc + 8;
+ gen_op_movl_TN_im[0](val);
+ } else {
+ gen_movl_T0_reg(s, i);
+ }
+ gen_op_stl_T0_T1();
+ }
+ j++;
+ /* no need to add after the last transfer */
+ if (j != n)
+ gen_op_addl_T1_im(4);
}
- if (!(insn & (1 << 24)))
- gen_op_addl_T1_im(val);
+ }
+ if (insn & (1 << 21)) {
+ /* write back */
+ if (insn & (1 << 23)) {
+ if (insn & (1 << 24)) {
+ /* pre increment */
+ } else {
+ /* post increment */
+ gen_op_addl_T1_im(4);
+ }
+ } else {
+ if (insn & (1 << 24)) {
+ /* pre decrement */
+ if (n != 1)
+ gen_op_addl_T1_im(-((n - 1) * 4));
+ } else {
+ /* post decrement */
+ gen_op_addl_T1_im(-(n * 4));
+ }
+ }
+ gen_movl_reg_T1(s, rn);
}
}
- if (insn & (1 << 21))
- gen_movl_reg_T1(s, rn);
break;
case 0xa:
case 0xb:
@@ -641,6 +712,66 @@ static void disas_arm_insn(DisasContext *s)
gen_op_swi();
s->is_jmp = DISAS_JUMP;
break;
+ case 0xc:
+ case 0xd:
+ rd = (insn >> 12) & 0x7;
+ rn = (insn >> 16) & 0xf;
+ gen_movl_T1_reg(s, rn);
+ val = (insn) & 0xff;
+ if (!(insn & (1 << 23)))
+ val = -val;
+ switch((insn >> 8) & 0xf) {
+ case 0x1:
+ /* load/store */
+ if ((insn & (1 << 24)))
+ gen_op_addl_T1_im(val);
+ /* XXX: do it */
+ if (!(insn & (1 << 24)))
+ gen_op_addl_T1_im(val);
+ if (insn & (1 << 21))
+ gen_movl_reg_T1(s, rn);
+ break;
+ case 0x2:
+ {
+ int n, i;
+ /* load store multiple */
+ if ((insn & (1 << 24)))
+ gen_op_addl_T1_im(val);
+ switch(insn & 0x00408000) {
+ case 0x00008000: n = 1; break;
+ case 0x00400000: n = 2; break;
+ case 0x00408000: n = 3; break;
+ default: n = 4; break;
+ }
+ for(i = 0;i < n; i++) {
+ /* XXX: do it */
+ }
+ if (!(insn & (1 << 24)))
+ gen_op_addl_T1_im(val);
+ if (insn & (1 << 21))
+ gen_movl_reg_T1(s, rn);
+ }
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
+ case 0x0e:
+ /* float ops */
+ /* XXX: do it */
+ switch((insn >> 20) & 0xf) {
+ case 0x2: /* wfs */
+ break;
+ case 0x3: /* rfs */
+ break;
+ case 0x4: /* wfc */
+ break;
+ case 0x5: /* rfc */
+ break;
+ default:
+ goto illegal_op;
+ }
+ break;
default:
illegal_op:
gen_op_movl_T0_im((long)s->pc - 4);
@@ -688,15 +819,19 @@ static inline int gen_intermediate_code_internal(TranslationBlock *tb, int searc
disas_arm_insn(dc);
} while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
(dc->pc - pc_start) < (TARGET_PAGE_SIZE - 32));
- /* we must store the eflags state if it is not already done */
- if (dc->is_jmp != DISAS_TB_JUMP &&
- dc->is_jmp != DISAS_JUMP) {
- gen_op_movl_T0_im((long)dc->pc - 4);
- gen_op_movl_reg_TN[0][15]();
- }
- if (dc->is_jmp != DISAS_TB_JUMP) {
+ switch(dc->is_jmp) {
+ case DISAS_JUMP_NEXT:
+ case DISAS_NEXT:
+ gen_op_jmp((long)dc->tb, (long)dc->pc);
+ break;
+ default:
+ case DISAS_JUMP:
/* indicate that the hash table must be used to find the next TB */
gen_op_movl_T0_0();
+ break;
+ case DISAS_TB_JUMP:
+ /* nothing more to generate */
+ break;
}
*gen_opc_ptr = INDEX_op_end;
@@ -756,5 +891,10 @@ void cpu_arm_dump_state(CPUARMState *env, FILE *f, int flags)
else
fprintf(f, " ");
}
- fprintf(f, "CPSR=%08x", env->cpsr);
+ fprintf(f, "PSR=%08x %c%c%c%c\n",
+ env->cpsr,
+ env->cpsr & (1 << 31) ? 'N' : '-',
+ env->cpsr & (1 << 30) ? 'Z' : '-',
+ env->cpsr & (1 << 29) ? 'C' : '-',
+ env->cpsr & (1 << 28) ? 'V' : '-');
}