/* thumbemu.c -- Thumb instruction emulation. Copyright (C) 1996, Cygnus Software Technologies Ltd. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . */ /* We can provide simple Thumb simulation by decoding the Thumb instruction into its corresponding ARM instruction, and using the existing ARM simulator. */ #ifndef MODET /* required for the Thumb instruction support */ #if 1 #error "MODET needs to be defined for the Thumb world to work" #else #define MODET (1) #endif #endif #include "armdefs.h" #include "armemu.h" #include "armos.h" #define tBIT(n) ( (ARMword)(tinstr >> (n)) & 1) #define tBITS(m,n) ( (ARMword)(tinstr << (31 - (n))) >> ((31 - (n)) + (m)) ) #define ntBIT(n) ( (ARMword)(next_instr >> (n)) & 1) #define ntBITS(m,n) ( (ARMword)(next_instr << (31 - (n))) >> ((31 - (n)) + (m)) ) static int test_cond (int cond, ARMul_State * state) { switch (cond) { case EQ: return ZFLAG; case NE: return !ZFLAG; case VS: return VFLAG; case VC: return !VFLAG; case MI: return NFLAG; case PL: return !NFLAG; case CS: return CFLAG; case CC: return !CFLAG; case HI: return (CFLAG && !ZFLAG); case LS: return (!CFLAG || ZFLAG); case GE: return ((!NFLAG && !VFLAG) || (NFLAG && VFLAG)); case LT: return ((NFLAG && !VFLAG) || (!NFLAG && VFLAG)); case GT: return ((!NFLAG && !VFLAG && !ZFLAG) || (NFLAG && VFLAG && !ZFLAG)); case LE: return ((NFLAG && !VFLAG) || (!NFLAG && VFLAG)) || ZFLAG; case AL: return TRUE; case NV: default: return FALSE; } } static ARMword skipping_32bit_thumb = 0; static int IT_block_cond = AL; static ARMword IT_block_mask = 0; static int IT_block_first = FALSE; static void handle_IT_block (ARMul_State * state, ARMword tinstr, tdstate * pvalid) { * pvalid = t_branch; IT_block_mask = tBITS (0, 3); if (IT_block_mask == 0) // NOP or a HINT. return; IT_block_cond = tBITS (4, 7); IT_block_first = TRUE; } static int in_IT_block (void) { return IT_block_mask != 0; } static int IT_block_allow (ARMul_State * state) { int cond; if (IT_block_mask == 0) return TRUE; cond = IT_block_cond; if (IT_block_first) IT_block_first = FALSE; else { if ((IT_block_mask & 8) == 0) cond &= 0xe; else cond |= 1; IT_block_mask <<= 1; IT_block_mask &= 0xF; } if (IT_block_mask == 0x8) IT_block_mask = 0; return test_cond (cond, state); } static ARMword ThumbExpandImm (ARMword tinstr) { ARMword val; if (tBITS (10, 11) == 0) { switch (tBITS (8, 9)) { case 0: val = tBITS (0, 7); break; case 1: val = tBITS (0, 7) << 8; break; case 2: val = (tBITS (0, 7) << 8) | (tBITS (0, 7) << 24); break; case 3: val = tBITS (0, 7) * 0x01010101; break; default: val = 0; } } else { int ror = tBITS (7, 11); val = (1 << 7) | tBITS (0, 6); val = (val >> ror) | (val << (32 - ror)); } return val; } #define tASSERT(truth) \ do \ { \ if (! (truth)) \ { \ fprintf (stderr, "unhandled T2 insn %04x|%04x detected at thumbemu.c:%d\n", \ tinstr, next_instr, __LINE__); \ return ; \ } \ } \ while (0) /* Attempt to emulate a 32-bit ARMv7 Thumb instruction. Stores t_branch into PVALUE upon success or t_undefined otherwise. */ static void handle_T2_insn (ARMul_State * state, ARMword tinstr, ARMword next_instr, ARMword pc, ARMword * ainstr, tdstate * pvalid) { * pvalid = t_undefined; if (! state->is_v6) return; if (trace) fprintf (stderr, "|%04x ", next_instr); if (tBITS (11, 15) == 0x1E && ntBIT (15) == 1) { ARMsword simm32 = 0; int S = tBIT (10); * pvalid = t_branch; switch ((ntBIT (14) << 1) | ntBIT (12)) { case 0: /* B.W */ { ARMword cond = tBITS (6, 9); ARMword imm6; ARMword imm11; ARMword J1; ARMword J2; tASSERT (cond != AL && cond != NV); if (! test_cond (cond, state)) return; imm6 = tBITS (0, 5); imm11 = ntBITS (0, 10); J1 = ntBIT (13); J2 = ntBIT (11); simm32 = (J1 << 19) | (J2 << 18) | (imm6 << 12) | (imm11 << 1); if (S) simm32 |= -(1 << 20); break; } case 1: /* B.W */ { ARMword imm10 = tBITS (0, 9); ARMword imm11 = ntBITS (0, 10); ARMword I1 = (ntBIT (13) ^ S) ? 0 : 1; ARMword I2 = (ntBIT (11) ^ S) ? 0 : 1; simm32 = (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); if (S) simm32 |= -(1 << 24); break; } case 2: /* BLX