aboutsummaryrefslogtreecommitdiffstats
path: root/target-arm/translate.c
diff options
context:
space:
mode:
authorpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2006-04-09 14:38:57 +0000
committerpbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>2006-04-09 14:38:57 +0000
commit358bf29e802e7f03d32dd4ba2064e1bba23a11fa (patch)
tree914a408b9f6964f80f84c53402858ca792772c42 /target-arm/translate.c
parentcdbdb648b7c2867f0bb7dce27efb1986f770dedb (diff)
Thumb prefetch abort fix.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1805 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'target-arm/translate.c')
-rw-r--r--target-arm/translate.c40
1 files changed, 38 insertions, 2 deletions
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 1f8a4853b..e6e8c6835 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -2295,8 +2295,21 @@ static void disas_thumb_insn(DisasContext *s)
case 14:
/* unconditional branch */
- if (insn & (1 << 11))
- goto undef; /* Second half of a blx */
+ if (insn & (1 << 11)) {
+ /* Second half of blx. */
+ offset = ((insn & 0x7ff) << 1);
+ gen_movl_T0_reg(s, 14);
+ gen_op_movl_T1_im(offset);
+ gen_op_addl_T0_T1();
+ gen_op_movl_T1_im(0xfffffffc);
+ gen_op_andl_T0_T1();
+
+ val = (uint32_t)s->pc;
+ gen_op_movl_T1_im(val | 1);
+ gen_movl_reg_T1(s, 14);
+ gen_bx(s);
+ break;
+ }
val = (uint32_t)s->pc;
offset = ((int32_t)insn << 21) >> 21;
val += (offset << 1) + 2;
@@ -2305,6 +2318,29 @@ static void disas_thumb_insn(DisasContext *s)
case 15:
/* branch and link [and switch to arm] */
+ if ((s->pc & ~TARGET_PAGE_MASK) == 0) {
+ /* Instruction spans a page boundary. Implement it as two
+ 16-bit instructions in case the second half causes an
+ prefetch abort. */
+ offset = ((int32_t)insn << 21) >> 9;
+ val = s->pc + 2 + offset;
+ gen_op_movl_T0_im(val);
+ gen_movl_reg_T0(s, 14);
+ break;
+ }
+ if (insn & (1 << 11)) {
+ /* Second half of bl. */
+ offset = ((insn & 0x7ff) << 1) | 1;
+ gen_movl_T0_reg(s, 14);
+ gen_op_movl_T1_im(offset);
+ gen_op_addl_T0_T1();
+
+ val = (uint32_t)s->pc;
+ gen_op_movl_T1_im(val | 1);
+ gen_movl_reg_T1(s, 14);
+ gen_bx(s);
+ break;
+ }
offset = ((int32_t)insn << 21) >> 10;
insn = lduw_code(s->pc);
offset |= insn & 0x7ff;