diff options
author | pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | 2006-04-09 14:38:57 +0000 |
---|---|---|
committer | pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | 2006-04-09 14:38:57 +0000 |
commit | 358bf29e802e7f03d32dd4ba2064e1bba23a11fa (patch) | |
tree | 914a408b9f6964f80f84c53402858ca792772c42 /target-arm | |
parent | cdbdb648b7c2867f0bb7dce27efb1986f770dedb (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')
-rw-r--r-- | target-arm/translate.c | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/target-arm/translate.c b/target-arm/translate.c index 1f8a4853bb..e6e8c6835a 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; |