/* 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