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