/*  iwmmxt.c -- Intel(r) Wireless MMX(tm) technology co-processor interface.
    Copyright (C) 2002, 2007, 2008, 2009, 2010, 2011
    Free Software Foundation, Inc.
    Contributed by matthew green (mrg@redhat.com).
 
    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 <http://www.gnu.org/licenses/>. */

#include <string.h>

#include "armdefs.h"
#include "armos.h"
#include "armemu.h"
#include "ansidecl.h"
#include "iwmmxt.h"

/* #define DEBUG 1 */

/* Intel(r) Wireless MMX(tm) technology co-processor.  
   It uses co-processor numbers (0 and 1).  There are 16 vector registers wRx
   and 16 control registers wCx.  Co-processors 0 and 1 are used in MCR/MRC
   to access wRx and wCx respectively.  */

static ARMdword wR[16];
static ARMword  wC[16] = { 0x69051010 };

#define SUBSTR(w,t,m,n) ((t)(w <<  ((sizeof (t) * 8 - 1) - (n))) \
                               >> (((sizeof (t) * 8 - 1) - (n)) + (m)))
#define wCBITS(w,x,y)   SUBSTR (wC[w], ARMword, x, y)
#define wRBITS(w,x,y)   SUBSTR (wR[w], ARMdword, x, y)
#define wCID   0
#define wCon   1
#define wCSSF  2
#define wCASF  3
#define wCGR0  8
#define wCGR1  9
#define wCGR2 10
#define wCGR3 11

/* Bits in the wCon register.  */
#define WCON_CUP	(1 << 0)
#define WCON_MUP	(1 << 1)

/* Set the SIMD wCASF flags for 8, 16, 32 or 64-bit operations.  */
#define SIMD8_SET(x,  v, n, b)	(x) |= ((v != 0) << ((((b) + 1) * 4) + (n)))
#define SIMD16_SET(x, v, n, h)	(x) |= ((v != 0) << ((((h) + 1) * 8) + (n)))
#define SIMD32_SET(x, v, n, w)	(x) |= ((v != 0) << ((((w) + 1) * 16) + (n)))
#define SIMD64_SET(x, v, n)	(x) |= ((v != 0) << (32 + (n)))

/* Flags to pass as "n" above.  */
#define SIMD_NBIT	-1
#define SIMD_ZBIT	-2
#define SIMD_CBIT	-3
#define SIMD_VBIT	-4

/* Various status bit macros.  */
#define NBIT8(x)	((x) & 0x80)
#define NBIT16(x)	((x) & 0x8000)
#define NBIT32(x)	((x) & 0x80000000)
#define NBIT64(x)	((x) & 0x8000000000000000ULL)
#define ZBIT8(x)	(((x) & 0xff) == 0)
#define ZBIT16(x)	(((x) & 0xffff) == 0)
#define ZBIT32(x)	(((x) & 0xffffffff) == 0)
#define ZBIT64(x)	(x == 0)

/* Access byte/half/word "n" of register "x".  */
#define wRBYTE(x,n)	wRBITS ((x), (n) * 8, (n) * 8 + 7)
#define wRHALF(x,n)	wRBITS ((x), (n) * 16, (n) * 16 + 15)
#define wRWORD(x,n)	wRBITS ((x), (n) * 32, (n) * 32 + 31)

/* Macro to handle how the G bit selects wCGR registers.  */
#define DECODE_G_BIT(state, instr, shift)	\
{						\
  unsigned int reg;				\
						\
  reg = BITS (0, 3);				\
						\
  if (BIT (8))	/* G */				\
    {						\
      if (reg < wCGR0 || reg > wCGR3)		\
	{					\
	  ARMul_UndefInstr (state, instr);	\
	  return ARMul_DONE;			\
	}					\
      shift = wC [reg];				\
    }						\
  else						\
    shift = wR [reg];				\
						\
  shift &= 0xff;				\
}

/* Index calculations for the satrv[] array.  */
#define BITIDX8(x)	(x)
#define BITIDX16(x)	(((x) + 1) * 2 - 1)
#define BITIDX32(x)	(((x) + 1) * 4 - 1)

/* Sign extension macros.  */
#define EXTEND8(a)	((a) & 0x80 ? ((a) | 0xffffff00) : (a))
#define EXTEND16(a)	((a) & 0x8000 ? ((a) | 0xffff0000) : (a))
#define EXTEND32(a)	((a) & 0x80000000ULL ? ((a) | 0xffffffff00000000ULL) : (a))

/* Set the wCSSF from 8 values.  */
#define SET_wCSSF(a,b,c,d,e,f,g,h) \
  wC[wCSSF] = (((h) != 0) << 7) | (((g) != 0) << 6) \
            | (((f) != 0) << 5) | (((e) != 0) << 4) \
            | (((d) != 0) << 3) | (((c) != 0) << 2) \
            | (((b) != 0) << 1) | (((a) != 0) << 0);

/* Set the wCSSR from an array with 8 values.  */
#define SET_wCSSFvec(v) \
  SET_wCSSF((v)[0],(v)[1],(v)[2],(v)[3],(v)[4],(v)[5],(v)[6],(v)[7])

/* Size qualifiers for vector operations.  */
#define Bqual 			0
#define Hqual 			1
#define Wqual 			2
#define Dqual 			3

/* Saturation qualifiers for vector operations.  */
#define NoSaturation 		0
#define UnsignedSaturation	1
#define SignedSaturation	3


/* Prototypes.  */
static ARMword         Add32  (ARMword,  ARMword,  int *, int *, ARMword);
static ARMdword        AddS32 (ARMdword, ARMdword, int *, int *);
static ARMdword        AddU32 (ARMdword, ARMdword, int *, int *);
static ARMword         AddS16 (ARMword,  ARMword,  int *, int *);
static ARMword         AddU16 (ARMword,  ARMword,  int *, int *);
static ARMword         AddS8  (ARMword,  ARMword,  int *, int *);
static ARMword         AddU8  (ARMword,  ARMword,  int *, int *);
static ARMword         Sub32  (ARMword,  ARMword,  int *, int *, ARMword);
static ARMdword        SubS32 (ARMdword, ARMdword, int *, int *);
static ARMdword        SubU32 (ARMdword, ARMdword, int *, int *);
static ARMword         SubS16 (ARMword,  ARMword,  int *, int *);
static ARMword         SubS8  (ARMword,  ARMword,  int *, int *);
static ARMword         SubU16 (ARMword,  ARMword,  int *, int *);
static ARMword         SubU8  (ARMword,  ARMword,  int *, int *);
static unsigned char   IwmmxtSaturateU8  (signed short, int *);
static signed char     IwmmxtSaturateS8  (signed short, int *);
static unsigned short  IwmmxtSaturateU16 (signed int, int *);
static signed short    IwmmxtSaturateS16 (signed int, int *);
static unsigned long   IwmmxtSaturateU32 (signed long long, int *);
static signed long     IwmmxtSaturateS32 (signed long long, int *);
static ARMword         Compute_Iwmmxt_Address   (ARMul_State *, ARMword, int *);
static ARMdword        Iwmmxt_Load_Double_Word  (ARMul_State *, ARMword);
static ARMword         Iwmmxt_Load_Word         (ARMul_State *, ARMword);
static ARMword         Iwmmxt_Load_Half_Word    (ARMul_State *, ARMword);
static ARMword         Iwmmxt_Load_Byte         (ARMul_State *, ARMword);
static void            Iwmmxt_Store_Double_Word (ARMul_State *, ARMword, ARMdword);
static void            Iwmmxt_Store_Word        (ARMul_State *, ARMword, ARMword);
static void            Iwmmxt_Store_Half_Word   (ARMul_State *, ARMword, ARMword);
static void            Iwmmxt_Store_Byte        (ARMul_State *, ARMword, ARMword);
static int             Process_Instruction      (ARMul_State *, ARMword);

static int TANDC    (ARMul_State *, ARMword);
static int TBCST    (ARMul_State *, ARMword);
static int TEXTRC   (ARMul_State *, ARMword);
static int TEXTRM   (ARMul_State *, ARMword);
static int TINSR    (ARMul_State *, ARMword);
static int TMCR     (ARMul_State *, ARMword);
static int TMCRR    (ARMul_State *, ARMword);
static int TMIA     (ARMul_State *, ARMword);
static int TMIAPH   (ARMul_State *, ARMword);
static int TMIAxy   (ARMul_State *, ARMword);
static int TMOVMSK  (ARMul_State *, ARMword);
static int TMRC     (ARMul_State *, ARMword);
static int TMRRC    (ARMul_State *, ARMword);
static int TORC     (ARMul_State *, ARMword);
static int WACC     (ARMul_State *, ARMword);
static int WADD     (ARMul_State *, ARMword);
static int WALIGNI  (ARMword);
static int WALIGNR  (ARMul_State *, ARMword);
static int WAND     (ARMword);
static int WANDN    (ARMword);
static int WAVG2    (ARMword);
static int WCMPEQ   (ARMul_State *, ARMword);
static int WCMPGT   (ARMul_State *, ARMword);
static int WLDR     (ARMul_State *, ARMword);
static int WMAC     (ARMword);
static int WMADD    (ARMword);
static int WMAX     (ARMul_State *, ARMword);
static int WMIN     (ARMul_State *, ARMword);
static int WMUL     (ARMword);
static int WOR      (ARMword);
static int WPACK    (ARMul_State *, ARMword);
static int WROR     (ARMul_State *, ARMword);
static int WSAD     (ARMword);
static int WSHUFH   (ARMword);
static int WSLL     (ARMul_State *, ARMword);
static int WSRA     (ARMul_State *, ARMword);
static int WSRL     (ARMul_State *, ARMword);
static int WSTR     (ARMul_State *, ARMword);
static int WSUB     (ARMul_State *, ARMword);
static int WUNPCKEH (ARMul_State *, ARMword);
static int WUNPCKEL (ARMul_State *, ARMword);
static int WUNPCKIH (ARMul_State *, ARMword);
static int WUNPCKIL (ARMul_State *, ARMword);
static int WXOR     (ARMword);

/* This function does the work of adding two 32bit values
   together, and calculating if a carry has occurred.  */

static ARMword
Add32 (ARMword a1,
       ARMword a2,
       int * carry_ptr,
       int * overflow_ptr,
       ARMword sign_mask)
{
  ARMword result = (a1 + a2);
  unsigned int uresult = (unsigned int) result;
  unsigned int ua1 = (unsigned int) a1;

  /* If (result == a1) and (a2 == 0),
     or (result > a2) then we have no carry.  */
  * carry_ptr = ((uresult == ua1) ? (a2 != 0) : (uresult < ua1));

  /* Overflow occurs when both arguments are the
     same sign, but the result is a different sign.  */
  * overflow_ptr = (   ( (result & sign_mask) && !(a1 & sign_mask) && !(a2 & sign_mask))
		    || (!(result & sign_mask) &&  (a1 & sign_mask) &&  (a2 & sign_mask)));
  
  return result;
}

static ARMdword
AddS32 (ARMdword a1, ARMdword a2, int * carry_ptr, int * overflow_ptr)
{
  ARMdword     result;
  unsigned int uresult;
  unsigned int ua1;

  a1 = EXTEND32 (a1);
  a2 = EXTEND32 (a2);

  result  = a1 + a2;
  uresult = (unsigned int) result;
  ua1     = (unsigned int) a1;

  * carry_ptr = ((uresult == a1) ? (a2 != 0) : (uresult < ua1));

  * overflow_ptr = (   ( (result & 0x80000000ULL) && !(a1 & 0x80000000ULL) && !(a2 & 0x80000000ULL))
		    || (!(result & 0x80000000ULL) &&  (a1 & 0x80000000ULL) &&  (a2 & 0x80000000ULL)));

  return result;
}

static ARMdword
AddU32 (ARMdword a1, ARMdword a2, int * carry_ptr, int * overflow_ptr)
{
  ARMdword     result;
  unsigned int uresult;
  unsigned int ua1;

  a1 &= 0xffffffff;
  a2 &= 0xffffffff;

  result  = a1 + a2;
  uresult = (unsigned int) result;
  ua1     = (unsigned int) a1;

  * carry_ptr = ((uresult == a1) ? (a2 != 0) : (uresult < ua1));

  * overflow_ptr = (   ( (result & 0x80000000ULL) && !(a1 & 0x80000000ULL) && !(a2 & 0x80000000ULL))
		    || (!(result & 0x80000000ULL) &&  (a1 & 0x80000000ULL) &&  (a2 & 0x80000000ULL)));

  return result;
}

static ARMword
AddS16 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr)
{
  a1 = EXTEND16 (a1);
  a2 = EXTEND16 (a2);

  return Add32 (a1, a2, carry_ptr, overflow_ptr, 0x8000);
}

static ARMword
AddU16 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr)
{
  a1 &= 0xffff;
  a2 &= 0xffff;

  return Add32 (a1, a2, carry_ptr, overflow_ptr, 0x8000);
}

static ARMword
AddS8 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr)
{
  a1 = EXTEND8 (a1);
  a2 = EXTEND8 (a2);

  return Add32 (a1, a2, carry_ptr, overflow_ptr, 0x80);
}

static ARMword
AddU8 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr)
{
  a1 &= 0xff;
  a2 &= 0xff;

  return Add32 (a1, a2, carry_ptr, overflow_ptr, 0x80);
}

static ARMword
Sub32 (ARMword a1,
       ARMword a2,
       int * borrow_ptr,
       int * overflow_ptr,
       ARMword sign_mask)
{
  ARMword result = (a1 - a2);
  unsigned int ua1 = (unsigned int) a1;
  unsigned int ua2 = (unsigned int) a2;

  /* A borrow occurs if a2 is (unsigned) larger than a1.
     However the carry flag is *cleared* if a borrow occurs.  */
  * borrow_ptr = ! (ua2 > ua1);

  /* Overflow occurs when a negative number is subtracted from a
     positive number and the result is negative or a positive
     number is subtracted from a negative number and the result is
     positive.  */
  * overflow_ptr = ( (! (a1 & sign_mask) &&   (a2 & sign_mask) &&   (result & sign_mask))
		    || ((a1 & sign_mask) && ! (a2 & sign_mask) && ! (result & sign_mask)));

  return result;
}

static ARMdword
SubS32 (ARMdword a1, ARMdword a2, int * borrow_ptr, int * overflow_ptr)
{
  ARMdword     result;
  unsigned int ua1;
  unsigned int ua2;

  a1 = EXTEND32 (a1);
  a2 = EXTEND32 (a2);

  result = a1 - a2;
  ua1    = (unsigned int) a1;
  ua2    = (unsigned int) a2;

  * borrow_ptr = ! (ua2 > ua1);

  * overflow_ptr = ( (! (a1 & 0x80000000ULL) &&   (a2 & 0x80000000ULL) &&   (result & 0x80000000ULL))
		    || ((a1 & 0x80000000ULL) && ! (a2 & 0x80000000ULL) && ! (result & 0x80000000ULL)));

  return result;
}

static ARMword
SubS16 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr)
{
  a1 = EXTEND16 (a1);
  a2 = EXTEND16 (a2);

  return Sub32 (a1, a2, carry_ptr, overflow_ptr, 0x8000);
}

static ARMword
SubS8 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr)
{
  a1 = EXTEND8 (a1);
  a2 = EXTEND8 (a2);

  return Sub32 (a1, a2, carry_ptr, overflow_ptr, 0x80);
}

static ARMword
SubU16 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr)
{
  a1 &= 0xffff;
  a2 &= 0xffff;

  return Sub32 (a1, a2, carry_ptr, overflow_ptr, 0x8000);
}

static ARMword
SubU8 (ARMword a1, ARMword a2, int * carry_ptr, int * overflow_ptr)
{
  a1 &= 0xff;
  a2 &= 0xff;

  return Sub32 (a1, a2, carry_ptr, overflow_ptr, 0x80);
}

static ARMdword
SubU32 (ARMdword a1, ARMdword a2, int * borrow_ptr, int * overflow_ptr)
{
  ARMdword     result;
  unsigned int ua1;
  unsigned int ua2;

  a1 &= 0xffffffff;
  a2 &= 0xffffffff;

  result = a1 - a2;
  ua1    = (unsigned int) a1;
  ua2    = (unsigned int) a2;

  * borrow_ptr = ! (ua2 > ua1);

  * overflow_ptr = ( (! (a1 & 0x80000000ULL) &&   (a2 & 0x80000000ULL) &&   (result & 0x80000000ULL))
		    || ((a1 & 0x80000000ULL) && ! (a2 & 0x80000000ULL) && ! (result & 0x80000000ULL)));

  return result;
}

/* For the saturation.  */

static unsigned char
IwmmxtSaturateU8 (signed short val, int * sat)
{
  unsigned char rv;

  if (val < 0)
    {
      rv = 0;
      *sat = 1;
    }
  else if (val > 0xff)
    {
      rv = 0xff;
      *sat = 1;
    }
  else
    {
      rv = val & 0xff;
      *sat = 0;
    }
  return rv;
}

static signed char
IwmmxtSaturateS8 (signed short val, int * sat)
{
  signed char rv;

  if (val < -0x80)
    {
      rv = -0x80;
      *sat = 1;
    }
  else if (val > 0x7f)
    {
      rv = 0x7f;
      *sat = 1;
    }
  else
    {
      rv = val & 0xff;
      *sat = 0;
    }
  return rv;
}

static unsigned short
IwmmxtSaturateU16 (signed int val, int * sat)
{
  unsigned short rv;

  if (val < 0)
    {
      rv = 0;
      *sat = 1;
    }
  else if (val > 0xffff)
    {
      rv = 0xffff;
      *sat = 1;
    }
  else
    {
      rv = val & 0xffff;
      *sat = 0;
    }
  return rv;
}

static signed short
IwmmxtSaturateS16 (signed int val, int * sat)
{
  signed short rv;
  
  if (val < -0x8000)
    {
      rv = - 0x8000;
      *sat = 1;
    }
  else if (val > 0x7fff)
    {
      rv = 0x7fff;
      *sat = 1;
    }
  else
    {
      rv = val & 0xffff;
      *sat = 0;
    }
  return rv;
}

static unsigned long
IwmmxtSaturateU32 (signed long long val, int * sat)
{
  unsigned long rv;

  if (val < 0)
    {
      rv = 0;
      *sat = 1;
    }
  else if (val > 0xffffffff)
    {
      rv = 0xffffffff;
      *sat = 1;
    }
  else
    {
      rv = val & 0xffffffff;
      *sat = 0;
    }
  return rv;
}

static signed long
IwmmxtSaturateS32 (signed long long val, int * sat)
{
  signed long rv;
  
  if (val < -0x80000000LL)
    {
      rv = -0x80000000;
      *sat = 1;
    }
  else if (val > 0x7fffffff)
    {
      rv = 0x7fffffff;
      *sat = 1;
    }
  else
    {
      rv = val & 0xffffffff;
      *sat = 0;
    }
  return rv;
}

/* Intel(r) Wireless MMX(tm) technology Acessor functions.  */

unsigned
IwmmxtLDC (ARMul_State * state ATTRIBUTE_UNUSED,
	   unsigned      type  ATTRIBUTE_UNUSED,
	   ARMword       instr,
	   ARMword       data)
{
  return ARMul_CANT;
}

unsigned
IwmmxtSTC (ARMul_State * state ATTRIBUTE_UNUSED,
	   unsigned      type  ATTRIBUTE_UNUSED,
	   ARMword       instr,
	   ARMword *     data)
{
  return ARMul_CANT;
}

unsigned
IwmmxtMRC (ARMul_State * state ATTRIBUTE_UNUSED,
	   unsigned      type  ATTRIBUTE_UNUSED,
	   ARMword       instr,
	   ARMword *     value)
{
  return ARMul_CANT;
}

unsigned
IwmmxtMCR (ARMul_State * state ATTRIBUTE_UNUSED,
	   unsigned      type  ATTRIBUTE_UNUSED,
	   ARMword       instr,
	   ARMword       value)
{
  return ARMul_CANT;
}

unsigned
IwmmxtCDP (ARMul_State * state, unsigned type, ARMword instr)
{
  return ARMul_CANT;
}

/* Intel(r) Wireless MMX(tm) technology instruction implementations.  */

static int
TANDC (ARMul_State * state, ARMword instr)
{
  ARMword cpsr;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tandc\n");
#endif  

  /* The Rd field must be r15.  */
  if (BITS (12, 15) != 15)
    return ARMul_CANT;

  /* The CRn field must be r3.  */
  if (BITS (16, 19) != 3)
    return ARMul_CANT;

  /* The CRm field must be r0.  */
  if (BITS (0, 3) != 0)
    return ARMul_CANT;

  cpsr = ARMul_GetCPSR (state) & 0x0fffffff;

  switch (BITS (22, 23))
    {
    case Bqual:
      cpsr |= (  (wCBITS (wCASF, 28, 31) & wCBITS (wCASF, 24, 27)
		& wCBITS (wCASF, 20, 23) & wCBITS (wCASF, 16, 19)
		& wCBITS (wCASF, 12, 15) & wCBITS (wCASF,  8, 11)
		& wCBITS (wCASF,  4,  7) & wCBITS (wCASF,  0,  3)) << 28);
      break;

    case Hqual:
      cpsr |= (  (wCBITS (wCASF, 28, 31) & wCBITS (wCASF, 20, 23)
		& wCBITS (wCASF, 12, 15) & wCBITS (wCASF,  4, 7)) << 28);
      break;

    case Wqual:
      cpsr |= ((wCBITS (wCASF, 28, 31) & wCBITS (wCASF, 12, 15)) << 28);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }
  
  ARMul_SetCPSR (state, cpsr);

  return ARMul_DONE;
}

static int
TBCST (ARMul_State * state, ARMword instr)
{
  ARMdword Rn;
  int wRd;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tbcst\n");
#endif  

  Rn  = state->Reg [BITS (12, 15)];
  if (BITS (12, 15) == 15)
    Rn &= 0xfffffffc;

  wRd = BITS (16, 19);

  switch (BITS (6, 7))
    {
    case Bqual:
      Rn &= 0xff;
      wR [wRd] = (Rn << 56) | (Rn << 48) | (Rn << 40) | (Rn << 32)
	       | (Rn << 24) | (Rn << 16) | (Rn << 8) | Rn;
      break;

    case Hqual:
      Rn &= 0xffff;
      wR [wRd] = (Rn << 48) | (Rn << 32) | (Rn << 16) | Rn;
      break;

    case Wqual:
      Rn &= 0xffffffff;
      wR [wRd] = (Rn << 32) | Rn;
      break;

    default:
      ARMul_UndefInstr (state, instr);
      break;
    }

  wC [wCon] |= WCON_MUP;
  return ARMul_DONE;
}

static int
TEXTRC (ARMul_State * state, ARMword instr)
{
  ARMword cpsr;
  ARMword selector;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "textrc\n");
#endif  

  /* The Rd field must be r15.  */
  if (BITS (12, 15) != 15)
    return ARMul_CANT;

  /* The CRn field must be r3.  */
  if (BITS (16, 19) != 3)
    return ARMul_CANT;

  /* The CRm field must be 0xxx.  */
  if (BIT (3) != 0)
    return ARMul_CANT;

  selector = BITS (0, 2);
  cpsr = ARMul_GetCPSR (state) & 0x0fffffff;

  switch (BITS (22, 23))
    {
    case Bqual: selector *= 4; break;
    case Hqual: selector = ((selector & 3) * 8) + 4; break;
    case Wqual: selector = ((selector & 1) * 16) + 12; break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }
  
  cpsr |= wCBITS (wCASF, selector, selector + 3) << 28;
  ARMul_SetCPSR (state, cpsr);

  return ARMul_DONE;
}

static int
TEXTRM (ARMul_State * state, ARMword instr)
{
  ARMword Rd;
  int     offset;
  int     wRn;
  int     sign;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "textrm\n");
#endif  

  wRn    = BITS (16, 19);
  sign   = BIT (3);
  offset = BITS (0, 2);
  
  switch (BITS (22, 23))
    {
    case Bqual:
      offset *= 8;
      Rd = wRBITS (wRn, offset, offset + 7);
      if (sign)
	Rd = EXTEND8 (Rd);
      break;

    case Hqual:
      offset = (offset & 3) * 16;
      Rd = wRBITS (wRn, offset, offset + 15);
      if (sign)
	Rd = EXTEND16 (Rd);
      break;

    case Wqual:
      offset = (offset & 1) * 32;
      Rd = wRBITS (wRn, offset, offset + 31);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  if (BITS (12, 15) == 15)
    ARMul_UndefInstr (state, instr);
  else
    state->Reg [BITS (12, 15)] = Rd;

  return ARMul_DONE;
}

static int
TINSR (ARMul_State * state, ARMword instr)
{
  ARMdword data;
  ARMword  offset;
  int      wRd;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tinsr\n");
#endif

  wRd = BITS (16, 19);
  data = state->Reg [BITS (12, 15)];
  offset = BITS (0, 2);

  switch (BITS (6, 7))
    {
    case Bqual:
      data &= 0xff;
      switch (offset)
	{
	case 0: wR [wRd] = data | (wRBITS (wRd, 8, 63) << 8); break;
	case 1: wR [wRd] = wRBITS (wRd, 0,  7) | (data <<  8) | (wRBITS (wRd, 16, 63) << 16); break;
	case 2: wR [wRd] = wRBITS (wRd, 0, 15) | (data << 16) | (wRBITS (wRd, 24, 63) << 24); break;
	case 3: wR [wRd] = wRBITS (wRd, 0, 23) | (data << 24) | (wRBITS (wRd, 32, 63) << 32); break;
	case 4: wR [wRd] = wRBITS (wRd, 0, 31) | (data << 32) | (wRBITS (wRd, 40, 63) << 40); break;
	case 5: wR [wRd] = wRBITS (wRd, 0, 39) | (data << 40) | (wRBITS (wRd, 48, 63) << 48); break;
	case 6: wR [wRd] = wRBITS (wRd, 0, 47) | (data << 48) | (wRBITS (wRd, 56, 63) << 56); break;
	case 7: wR [wRd] = wRBITS (wRd, 0, 55) | (data << 56); break;
	}
      break;

    case Hqual:
      data &= 0xffff;

      switch (offset & 3)
	{
	case 0: wR [wRd] = data | (wRBITS (wRd, 16, 63) << 16); break;	  
	case 1: wR [wRd] = wRBITS (wRd, 0, 15) | (data << 16) | (wRBITS (wRd, 32, 63) << 32); break;
	case 2: wR [wRd] = wRBITS (wRd, 0, 31) | (data << 32) | (wRBITS (wRd, 48, 63) << 48); break;
	case 3: wR [wRd] = wRBITS (wRd, 0, 47) | (data << 48); break;
	}
      break;

    case Wqual:
      if (offset & 1)
	wR [wRd] = wRBITS (wRd, 0, 31) | (data << 32);
      else
	wR [wRd] = (wRBITS (wRd, 32, 63) << 32) | data;
      break;

    default:
      ARMul_UndefInstr (state, instr);
      break;
    }

  wC [wCon] |= WCON_MUP;
  return ARMul_DONE;
}

static int
TMCR (ARMul_State * state, ARMword instr)
{
  ARMword val;
  int     wCreg;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tmcr\n");
#endif  

  if (BITS (0, 3) != 0)
    return ARMul_CANT;

  val = state->Reg [BITS (12, 15)];
  if (BITS (12, 15) == 15)
    val &= 0xfffffffc;

  wCreg = BITS (16, 19);

  switch (wCreg)
    {
    case wCID:
      /* The wCID register is read only.  */
      break;

    case wCon:
      /* Writing to the MUP or CUP bits clears them.  */
      wC [wCon] &= ~ (val & 0x3);
      break;
      
    case wCSSF:
      /* Only the bottom 8 bits can be written to.
          The higher bits write as zero.  */
      wC [wCSSF] = (val & 0xff);
      wC [wCon] |= WCON_CUP;
      break;
      
    default:
      wC [wCreg] = val;
      wC [wCon] |= WCON_CUP;
      break;
    }

  return ARMul_DONE;
}

static int
TMCRR (ARMul_State * state, ARMword instr)
{
  ARMdword RdHi = state->Reg [BITS (16, 19)];
  ARMword  RdLo = state->Reg [BITS (12, 15)];

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tmcrr\n");
#endif  

  if ((BITS (16, 19) == 15) || (BITS (12, 15) == 15))
    return ARMul_CANT;

  wR [BITS (0, 3)] = (RdHi << 32) | RdLo;

  wC [wCon] |= WCON_MUP;

  return ARMul_DONE;
}

static int
TMIA (ARMul_State * state, ARMword instr)
{
  signed long long a, b;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tmia\n");
#endif  

  if ((BITS (0, 3) == 15) || (BITS (12, 15) == 15))
    {
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  a = state->Reg [BITS (0, 3)];
  b = state->Reg [BITS (12, 15)];

  a = EXTEND32 (a);
  b = EXTEND32 (b);

  wR [BITS (5, 8)] += a * b;
  wC [wCon] |= WCON_MUP;

  return ARMul_DONE;
}

static int
TMIAPH (ARMul_State * state, ARMword instr)
{
  signed long a, b, result;
  signed long long r;
  ARMword Rm = state->Reg [BITS (0, 3)];
  ARMword Rs = state->Reg [BITS (12, 15)];
  
  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tmiaph\n");
#endif  

  if (BITS (0, 3) == 15 || BITS (12, 15) == 15)
    {
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  a = SUBSTR (Rs, ARMword, 16, 31);
  b = SUBSTR (Rm, ARMword, 16, 31);

  a = EXTEND16 (a);
  b = EXTEND16 (b);

  result = a * b;

  r = result;
  r = EXTEND32 (r);
  
  wR [BITS (5, 8)] += r;

  a = SUBSTR (Rs, ARMword,  0, 15);
  b = SUBSTR (Rm, ARMword,  0, 15);

  a = EXTEND16 (a);
  b = EXTEND16 (b);

  result = a * b;

  r = result;
  r = EXTEND32 (r);
  
  wR [BITS (5, 8)] += r;
  wC [wCon] |= WCON_MUP;

  return ARMul_DONE;
}

static int
TMIAxy (ARMul_State * state, ARMword instr)
{
  ARMword Rm;
  ARMword Rs;
  long long temp;
  
  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tmiaxy\n");
#endif  

  if (BITS (0, 3) == 15 || BITS (12, 15) == 15)
    {
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  Rm = state->Reg [BITS (0, 3)];
  if (BIT (17))
    Rm >>= 16;
  else
    Rm &= 0xffff;

  Rs = state->Reg [BITS (12, 15)];
  if (BIT (16))
    Rs >>= 16;
  else
    Rs &= 0xffff;

  if (Rm & (1 << 15))
    Rm -= 1 << 16;

  if (Rs & (1 << 15))
    Rs -= 1 << 16;

  Rm *= Rs;
  temp = Rm;

  if (temp & (1 << 31))
    temp -= 1ULL << 32;

  wR [BITS (5, 8)] += temp;
  wC [wCon] |= WCON_MUP;

  return ARMul_DONE;
}

static int
TMOVMSK (ARMul_State * state, ARMword instr)
{
  ARMdword result;
  int      wRn;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tmovmsk\n");
#endif  

  /* The CRm field must be r0.  */
  if (BITS (0, 3) != 0)
    return ARMul_CANT;

  wRn = BITS (16, 19);

  switch (BITS (22, 23))
    {
    case Bqual:
      result = (  (wRBITS (wRn, 63, 63) << 7)
		| (wRBITS (wRn, 55, 55) << 6)
		| (wRBITS (wRn, 47, 47) << 5)
		| (wRBITS (wRn, 39, 39) << 4)
		| (wRBITS (wRn, 31, 31) << 3)
		| (wRBITS (wRn, 23, 23) << 2)
		| (wRBITS (wRn, 15, 15) << 1)
		| (wRBITS (wRn,  7,  7) << 0));
      break;

    case Hqual:
      result = (  (wRBITS (wRn, 63, 63) << 3)
		| (wRBITS (wRn, 47, 47) << 2)
		| (wRBITS (wRn, 31, 31) << 1)
		| (wRBITS (wRn, 15, 15) << 0));
      break;

    case Wqual:
      result = (wRBITS (wRn, 63, 63) << 1) | wRBITS (wRn, 31, 31);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  state->Reg [BITS (12, 15)] = result;

  return ARMul_DONE;
}

static int
TMRC (ARMul_State * state, ARMword instr)
{
  int reg = BITS (12, 15);

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tmrc\n");
#endif  

  if (BITS (0, 3) != 0)
    return ARMul_CANT;

  if (reg == 15)
    ARMul_UndefInstr (state, instr);
  else
    state->Reg [reg] = wC [BITS (16, 19)];

  return ARMul_DONE;
}

static int
TMRRC (ARMul_State * state, ARMword instr)
{
  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "tmrrc\n");
#endif  

  if ((BITS (16, 19) == 15) || (BITS (12, 15) == 15) || (BITS (4, 11) != 0))
    ARMul_UndefInstr (state, instr);
  else
    {
      state->Reg [BITS (16, 19)] = wRBITS (BITS (0, 3), 32, 63);
      state->Reg [BITS (12, 15)] = wRBITS (BITS (0, 3),  0, 31);
    }

  return ARMul_DONE;
}

static int
TORC (ARMul_State * state, ARMword instr)
{
  ARMword cpsr = ARMul_GetCPSR (state);

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "torc\n");
#endif  

  /* The Rd field must be r15.  */
  if (BITS (12, 15) != 15)
    return ARMul_CANT;
  
  /* The CRn field must be r3.  */
  if (BITS (16, 19) != 3)
    return ARMul_CANT;
  
  /* The CRm field must be r0.  */
  if (BITS (0, 3) != 0)
    return ARMul_CANT;

  cpsr &= 0x0fffffff;

  switch (BITS (22, 23))
    {
    case Bqual:
      cpsr |= (  (wCBITS (wCASF, 28, 31) | wCBITS (wCASF, 24, 27)
		| wCBITS (wCASF, 20, 23) | wCBITS (wCASF, 16, 19)
		| wCBITS (wCASF, 12, 15) | wCBITS (wCASF,  8, 11)
		| wCBITS (wCASF,  4,  7) | wCBITS (wCASF,  0,  3)) << 28);
      break;

    case Hqual:
      cpsr |= (  (wCBITS (wCASF, 28, 31) | wCBITS (wCASF, 20, 23)
		| wCBITS (wCASF, 12, 15) | wCBITS (wCASF,  4,  7)) << 28);
      break;

    case Wqual:
      cpsr |= ((wCBITS (wCASF, 28, 31) | wCBITS (wCASF, 12, 15)) << 28);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }
  
  ARMul_SetCPSR (state, cpsr);

  return ARMul_DONE;
}

static int
WACC (ARMul_State * state, ARMword instr)
{
  int wRn;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wacc\n");
#endif  

  wRn = BITS (16, 19);

  switch (BITS (22, 23))
    {
    case Bqual:
      wR [BITS (12, 15)] =
	  wRBITS (wRn, 56, 63) + wRBITS (wRn, 48, 55)
	+ wRBITS (wRn, 40, 47) + wRBITS (wRn, 32, 39)
	+ wRBITS (wRn, 24, 31) + wRBITS (wRn, 16, 23)
	+ wRBITS (wRn,  8, 15) + wRBITS (wRn,  0,  7);
      break;

    case Hqual:
      wR [BITS (12, 15)] =
	  wRBITS (wRn, 48, 63) + wRBITS (wRn, 32, 47)
	+ wRBITS (wRn, 16, 31) + wRBITS (wRn,  0, 15);
      break;

    case Wqual:
      wR [BITS (12, 15)] = wRBITS (wRn, 32, 63) + wRBITS (wRn, 0, 31);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      break;
    }

  wC [wCon] |= WCON_MUP;
  return ARMul_DONE;
}

static int
WADD (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMdword x;
  ARMdword s;
  ARMword  psr = 0;
  int      i;
  int      carry;
  int      overflow;
  int      satrv[8];

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wadd\n");
#endif  

  /* Add two numbers using the specified function,
     leaving setting the carry bit as required.  */
#define ADDx(x, y, m, f) \
   (*f) (wRBITS (BITS (16, 19), (x), (y)) & (m), \
         wRBITS (BITS ( 0,  3), (x), (y)) & (m), \
        & carry, & overflow)

  switch (BITS (22, 23))
    {
    case Bqual:
      for (i = 0; i < 8; i++)
        {
	  switch (BITS (20, 21))
	    {
	    case NoSaturation:
	      s = ADDx ((i * 8), (i * 8) + 7, 0xff, AddS8);
	      satrv [BITIDX8 (i)] = 0;
	      r |= (s & 0xff) << (i * 8);
	      SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i);
	      SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i);
	      SIMD8_SET (psr, carry,     SIMD_CBIT, i);
	      SIMD8_SET (psr, overflow,  SIMD_VBIT, i);
	      break;

	    case UnsignedSaturation:
	      s = ADDx ((i * 8), (i * 8) + 7, 0xff, AddU8);
	      x = IwmmxtSaturateU8 (s, satrv + BITIDX8 (i));
	      r |= (x & 0xff) << (i * 8);
	      SIMD8_SET (psr, NBIT8 (x), SIMD_NBIT, i);
	      SIMD8_SET (psr, ZBIT8 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX8 (i)])
		{
		  SIMD8_SET (psr, carry,    SIMD_CBIT, i);
		  SIMD8_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    case SignedSaturation:
	      s = ADDx ((i * 8), (i * 8) + 7, 0xff, AddS8);
	      x = IwmmxtSaturateS8 (s, satrv + BITIDX8 (i));
	      r |= (x & 0xff) << (i * 8);
	      SIMD8_SET (psr, NBIT8 (x), SIMD_NBIT, i);
	      SIMD8_SET (psr, ZBIT8 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX8 (i)])
		{
		  SIMD8_SET (psr, carry,    SIMD_CBIT, i);
		  SIMD8_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    default:
	      ARMul_UndefInstr (state, instr);
	      return ARMul_DONE;
	    }
	}
      break;

    case Hqual:
      satrv[0] = satrv[2] = satrv[4] = satrv[6] = 0;

      for (i = 0; i < 4; i++)
	{
	  switch (BITS (20, 21))
	    {
	    case NoSaturation:
	      s = ADDx ((i * 16), (i * 16) + 15, 0xffff, AddS16);
	      satrv [BITIDX16 (i)] = 0;
	      r |= (s & 0xffff) << (i * 16);
	      SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	      SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	      SIMD16_SET (psr, carry,      SIMD_CBIT, i);
	      SIMD16_SET (psr, overflow,   SIMD_VBIT, i);
	      break;

	    case UnsignedSaturation:
	      s = ADDx ((i * 16), (i * 16) + 15, 0xffff, AddU16);
	      x = IwmmxtSaturateU16 (s, satrv + BITIDX16 (i));
	      r |= (x & 0xffff) << (i * 16);
	      SIMD16_SET (psr, NBIT16 (x), SIMD_NBIT, i);
	      SIMD16_SET (psr, ZBIT16 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX16 (i)])
		{
		  SIMD16_SET (psr, carry,    SIMD_CBIT, i);
		  SIMD16_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    case SignedSaturation:
	      s = ADDx ((i * 16), (i * 16) + 15, 0xffff, AddS16);
	      x = IwmmxtSaturateS16 (s, satrv + BITIDX16 (i));
	      r |= (x & 0xffff) << (i * 16);
	      SIMD16_SET (psr, NBIT16 (x), SIMD_NBIT, i);
	      SIMD16_SET (psr, ZBIT16 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX16 (i)])
		{
		  SIMD16_SET (psr, carry,    SIMD_CBIT, i);
		  SIMD16_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    default:
	      ARMul_UndefInstr (state, instr);
	      return ARMul_DONE;
	    }
	}
      break;

    case Wqual:
      satrv[0] = satrv[1] = satrv[2] = satrv[4] = satrv[5] = satrv[6] = 0;

      for (i = 0; i < 2; i++)
	{
	  switch (BITS (20, 21))
	    {
	    case NoSaturation:
	      s = ADDx ((i * 32), (i * 32) + 31, 0xffffffff, AddS32);
	      satrv [BITIDX32 (i)] = 0;
	      r |= (s & 0xffffffff) << (i * 32);
	      SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	      SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	      SIMD32_SET (psr, carry,      SIMD_CBIT, i);
	      SIMD32_SET (psr, overflow,   SIMD_VBIT, i);
	      break;

	    case UnsignedSaturation:
	      s = ADDx ((i * 32), (i * 32) + 31, 0xffffffff, AddU32);
	      x = IwmmxtSaturateU32 (s, satrv + BITIDX32 (i));
	      r |= (x & 0xffffffff) << (i * 32);
	      SIMD32_SET (psr, NBIT32 (x), SIMD_NBIT, i);
	      SIMD32_SET (psr, ZBIT32 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX32 (i)])
		{
		  SIMD32_SET (psr, carry,    SIMD_CBIT, i);
		  SIMD32_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    case SignedSaturation:
	      s = ADDx ((i * 32), (i * 32) + 31, 0xffffffff, AddS32);
	      x = IwmmxtSaturateS32 (s, satrv + BITIDX32 (i));
	      r |= (x & 0xffffffff) << (i * 32);
	      SIMD32_SET (psr, NBIT32 (x), SIMD_NBIT, i);
	      SIMD32_SET (psr, ZBIT32 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX32 (i)])
		{
		  SIMD32_SET (psr, carry,    SIMD_CBIT, i);
		  SIMD32_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    default:
	      ARMul_UndefInstr (state, instr);
	      return ARMul_DONE;
	    }
	}
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_MUP | WCON_CUP);

  SET_wCSSFvec (satrv);
  
#undef ADDx

  return ARMul_DONE;
}

static int
WALIGNI (ARMword instr)
{
  int shift = BITS (20, 22) * 8;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "waligni\n");
#endif  

  if (shift)
    wR [BITS (12, 15)] =
      wRBITS (BITS (16, 19), shift, 63)
      | (wRBITS (BITS (0, 3), 0, shift) << ((64 - shift)));
  else
    wR [BITS (12, 15)] = wR [BITS (16, 19)];
	   
  wC [wCon] |= WCON_MUP;
  return ARMul_DONE;
}

static int
WALIGNR (ARMul_State * state, ARMword instr)
{
  int shift = (wC [BITS (20, 21) + 8] & 0x7) * 8;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "walignr\n");
#endif  

  if (shift)
    wR [BITS (12, 15)] =
      wRBITS (BITS (16, 19), shift, 63)
      | (wRBITS (BITS (0, 3), 0, shift) << ((64 - shift)));
  else
    wR [BITS (12, 15)] = wR [BITS (16, 19)];

  wC [wCon] |= WCON_MUP;
  return ARMul_DONE;
}

static int
WAND (ARMword instr)
{
  ARMdword result;
  ARMword  psr = 0;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wand\n");
#endif  

  result = wR [BITS (16, 19)] & wR [BITS (0, 3)];
  wR [BITS (12, 15)] = result;

  SIMD64_SET (psr, (result == 0), SIMD_ZBIT);
  SIMD64_SET (psr, (result & (1ULL << 63)), SIMD_NBIT);
  
  wC [wCASF] = psr;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WANDN (ARMword instr)
{
  ARMdword result;
  ARMword  psr = 0;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wandn\n");
#endif  

  result = wR [BITS (16, 19)] & ~ wR [BITS (0, 3)];
  wR [BITS (12, 15)] = result;

  SIMD64_SET (psr, (result == 0), SIMD_ZBIT);
  SIMD64_SET (psr, (result & (1ULL << 63)), SIMD_NBIT);
  
  wC [wCASF] = psr;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WAVG2 (ARMword instr)
{
  ARMdword r = 0;
  ARMword  psr = 0;
  ARMdword s;
  int      i;
  int      round = BIT (20) ? 1 : 0;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wavg2\n");
#endif  

#define AVG2x(x, y, m) (((wRBITS (BITS (16, 19), (x), (y)) & (m)) \
		       + (wRBITS (BITS ( 0,  3), (x), (y)) & (m)) \
		       + round) / 2)

  if (BIT (22))
    {
      for (i = 0; i < 4; i++)
	{
	  s = AVG2x ((i * 16), (i * 16) + 15, 0xffff) & 0xffff;
	  SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	  r |= s << (i * 16);
	}
    }
  else
    {
      for (i = 0; i < 8; i++)
	{
	  s = AVG2x ((i * 8), (i * 8) + 7, 0xff) & 0xff;
	  SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i);
	  r |= s << (i * 8);
	}
    }

  wR [BITS (12, 15)] = r;
  wC [wCASF] = psr;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WCMPEQ (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMword  psr = 0;
  ARMdword s;
  int      i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wcmpeq\n");
#endif  

  switch (BITS (22, 23))
    {
    case Bqual:
      for (i = 0; i < 8; i++)
	{
	  s = wRBYTE (BITS (16, 19), i) == wRBYTE (BITS (0, 3), i) ? 0xff : 0;
	  r |= s << (i * 8);
	  SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i);
	  SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i);
	}
      break;

    case Hqual:
      for (i = 0; i < 4; i++)
	{
	  s = wRHALF (BITS (16, 19), i) == wRHALF (BITS (0, 3), i) ? 0xffff : 0;
	  r |= s << (i * 16);
	  SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	  SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	}
      break;

    case Wqual:
      for (i = 0; i < 2; i++)
	{
	  s = wRWORD (BITS (16, 19), i) == wRWORD (BITS (0, 3), i) ? 0xffffffff : 0;
	  r |= s << (i * 32);
	  SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	  SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	}
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WCMPGT (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMword  psr = 0;
  ARMdword s;
  int      i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wcmpgt\n");
#endif  

  switch (BITS (22, 23))
    {
    case Bqual:
      if (BIT (21))
	{
	  /* Use a signed comparison.  */
	  for (i = 0; i < 8; i++)
	    {
	      signed char a, b;
	      
	      a = wRBYTE (BITS (16, 19), i);
	      b = wRBYTE (BITS (0, 3), i);

	      s = (a > b) ? 0xff : 0;
	      r |= s << (i * 8);
	      SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i);
	      SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i);
	    }
	}
      else
	{
	  for (i = 0; i < 8; i++)
	    {
	      s = (wRBYTE (BITS (16, 19), i) > wRBYTE (BITS (0, 3), i))
		? 0xff : 0;
	      r |= s << (i * 8);
	      SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i);
	      SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i);
	    }
	}
      break;

    case Hqual:
      if (BIT (21))
	{
	  for (i = 0; i < 4; i++)
	    {
	      signed int a, b;

	      a = wRHALF (BITS (16, 19), i);
	      a = EXTEND16 (a);

	      b = wRHALF (BITS (0, 3), i);
	      b = EXTEND16 (b);

	      s = (a > b) ? 0xffff : 0;		
	      r |= s << (i * 16);
	      SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	      SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	    }
	}
      else
	{
	  for (i = 0; i < 4; i++)
	    {
	      s = (wRHALF (BITS (16, 19), i) > wRHALF (BITS (0, 3), i))
		? 0xffff : 0;
	      r |= s << (i * 16);
	      SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	      SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	    }
	}
      break;

    case Wqual:
      if (BIT (21))
	{
	  for (i = 0; i < 2; i++)
	    {
	      signed long a, b;

	      a = wRWORD (BITS (16, 19), i);
	      b = wRWORD (BITS (0, 3), i);

	      s = (a > b) ? 0xffffffff : 0;
	      r |= s << (i * 32);
	      SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	      SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	    }
	}
      else
	{
	  for (i = 0; i < 2; i++)
	    {
	      s = (wRWORD (BITS (16, 19), i) > wRWORD (BITS (0, 3), i))
		? 0xffffffff : 0;
	      r |= s << (i * 32);
	      SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	      SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	    }
	}
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static ARMword
Compute_Iwmmxt_Address (ARMul_State * state, ARMword instr, int * pFailed)
{
  ARMword  Rn;
  ARMword  addr;
  ARMword  offset;
  ARMword  multiplier;

  * pFailed  = 0;
  Rn         = BITS (16, 19);
  addr       = state->Reg [Rn];
  offset     = BITS (0, 7);
  multiplier = BIT (8) ? 4 : 1;

  if (BIT (24)) /* P */
    {
      /* Pre Indexed Addressing.  */
      if (BIT (23))
	addr += offset * multiplier;
      else
	addr -= offset * multiplier;

      /* Immediate Pre-Indexed.  */
      if (BIT (21)) /* W */
	{
	  if (Rn == 15)
	    {
	      /* Writeback into R15 is UNPREDICTABLE.  */
#ifdef DEBUG
	      fprintf (stderr, "iWMMXt: writeback into r15\n");
#endif
	      * pFailed = 1;
	    }
	  else
	    state->Reg [Rn] = addr;
	}
    }
  else
    {
      /* Post Indexed Addressing.  */
      if (BIT (21)) /* W */
	{
	  /* Handle the write back of the final address.  */
	  if (Rn == 15)
	    {
	      /* Writeback into R15 is UNPREDICTABLE.  */
#ifdef DEBUG
	      fprintf (stderr, "iWMMXt: writeback into r15\n");
#endif  
	      * pFailed = 1;
	    }
	  else
	    {
	      ARMword  increment;

	      if (BIT (23))
		increment = offset * multiplier;
	      else
		increment = - (offset * multiplier);

	      state->Reg [Rn] = addr + increment;
	    }
	}
      else
	{
	  /* P == 0, W == 0, U == 0 is UNPREDICTABLE.  */
	  if (BIT (23) == 0)
	    {
#ifdef DEBUG
	      fprintf (stderr, "iWMMXt: undefined addressing mode\n");
#endif  
	      * pFailed = 1;
	    }
	}
    }

  return addr;
}

static ARMdword
Iwmmxt_Load_Double_Word (ARMul_State * state, ARMword address)
{
  ARMdword value;
  
  /* The address must be aligned on a 8 byte boundary.  */
  if (address & 0x7)
    {
      fprintf (stderr, "iWMMXt: At addr 0x%x: Unaligned double word load from 0x%x\n",
	       (state->Reg[15] - 8) & ~0x3, address);
#ifdef DEBUG
#endif
      /* No need to check for alignment traps.  An unaligned
	 double word load with alignment trapping disabled is
	 UNPREDICTABLE.  */
      ARMul_Abort (state, ARMul_DataAbortV);
    }

  /* Load the words.  */
  if (! state->bigendSig)
    {
      value = ARMul_LoadWordN (state, address + 4);
      value <<= 32;
      value |= ARMul_LoadWordN (state, address);
    }
  else
    {
      value = ARMul_LoadWordN (state, address);
      value <<= 32;
      value |= ARMul_LoadWordN (state, address + 4);
    }

  /* Check for data aborts.  */
  if (state->Aborted)
    ARMul_Abort (state, ARMul_DataAbortV);
  else
    ARMul_Icycles (state, 2, 0L);

  return value;
}

static ARMword
Iwmmxt_Load_Word (ARMul_State * state, ARMword address)
{
  ARMword value;

  /* Check for a misaligned address.  */
  if (address & 3)
    {
      if ((read_cp15_reg (1, 0, 0) & ARMul_CP15_R1_ALIGN))
	ARMul_Abort (state, ARMul_DataAbortV);
      else
	address &= ~ 3;
    }
  
  value = ARMul_LoadWordN (state, address);

  if (state->Aborted)
    ARMul_Abort (state, ARMul_DataAbortV);
  else
    ARMul_Icycles (state, 1, 0L);

  return value;
}

static ARMword
Iwmmxt_Load_Half_Word (ARMul_State * state, ARMword address)
{
  ARMword value;

  /* Check for a misaligned address.  */
  if (address & 1)
    {
      if ((read_cp15_reg (1, 0, 0) & ARMul_CP15_R1_ALIGN))
	ARMul_Abort (state, ARMul_DataAbortV);
      else
	address &= ~ 1;
    }

  value = ARMul_LoadHalfWord (state, address);

  if (state->Aborted)
    ARMul_Abort (state, ARMul_DataAbortV);
  else
    ARMul_Icycles (state, 1, 0L);

  return value;
}

static ARMword
Iwmmxt_Load_Byte (ARMul_State * state, ARMword address)
{
  ARMword value;

  value = ARMul_LoadByte (state, address);

  if (state->Aborted)
    ARMul_Abort (state, ARMul_DataAbortV);
  else
    ARMul_Icycles (state, 1, 0L);

  return value;
}

static void
Iwmmxt_Store_Double_Word (ARMul_State * state, ARMword address, ARMdword value)
{
  /* The address must be aligned on a 8 byte boundary.  */
  if (address & 0x7)
    {
      fprintf (stderr, "iWMMXt: At addr 0x%x: Unaligned double word store to 0x%x\n",
	       (state->Reg[15] - 8) & ~0x3, address);
#ifdef DEBUG
#endif
      /* No need to check for alignment traps.  An unaligned
	 double word store with alignment trapping disabled is
	 UNPREDICTABLE.  */
      ARMul_Abort (state, ARMul_DataAbortV);
    }

  /* Store the words.  */
  if (! state->bigendSig)
    {
      ARMul_StoreWordN (state, address, value);
      ARMul_StoreWordN (state, address + 4, value >> 32);
    }
  else
    {
      ARMul_StoreWordN (state, address + 4, value);
      ARMul_StoreWordN (state, address, value >> 32);
    }

  /* Check for data aborts.  */
  if (state->Aborted)
    ARMul_Abort (state, ARMul_DataAbortV);
  else
    ARMul_Icycles (state, 2, 0L);
}

static void
Iwmmxt_Store_Word (ARMul_State * state, ARMword address, ARMword value)
{
  /* Check for a misaligned address.  */
  if (address & 3)
    {
      if ((read_cp15_reg (1, 0, 0) & ARMul_CP15_R1_ALIGN))
	ARMul_Abort (state, ARMul_DataAbortV);
      else
	address &= ~ 3;
    }

  ARMul_StoreWordN (state, address, value);

  if (state->Aborted)
    ARMul_Abort (state, ARMul_DataAbortV);
}

static void
Iwmmxt_Store_Half_Word (ARMul_State * state, ARMword address, ARMword value)
{
  /* Check for a misaligned address.  */
  if (address & 1)
    {
      if ((read_cp15_reg (1, 0, 0) & ARMul_CP15_R1_ALIGN))
	ARMul_Abort (state, ARMul_DataAbortV);
      else
	address &= ~ 1;
    }

  ARMul_StoreHalfWord (state, address, value);

  if (state->Aborted)
    ARMul_Abort (state, ARMul_DataAbortV);
}

static void
Iwmmxt_Store_Byte (ARMul_State * state, ARMword address, ARMword value)
{
  ARMul_StoreByte (state, address, value);

  if (state->Aborted)
    ARMul_Abort (state, ARMul_DataAbortV);
}

static int
WLDR (ARMul_State * state, ARMword instr)
{
  ARMword address;
  int failed;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wldr\n");
#endif  

  address = Compute_Iwmmxt_Address (state, instr, & failed);
  if (failed)
    return ARMul_CANT;

  if (BITS (28, 31) == 0xf)
    {
      /* WLDRW wCx */
      wC [BITS (12, 15)] = Iwmmxt_Load_Word (state, address);
    }
  else if (BIT (8) == 0)
    {
      if (BIT (22) == 0)
	/* WLDRB */
	wR [BITS (12, 15)] = Iwmmxt_Load_Byte (state, address);
      else
	/* WLDRH */
	wR [BITS (12, 15)] = Iwmmxt_Load_Half_Word (state, address);
    }
  else
    {
      if (BIT (22) == 0)
	/* WLDRW wRd */
	wR [BITS (12, 15)] = Iwmmxt_Load_Word (state, address);
      else
	/* WLDRD */
	wR [BITS (12, 15)] = Iwmmxt_Load_Double_Word (state, address);
    }

  wC [wCon] |= WCON_MUP;

  return ARMul_DONE;
}

static int
WMAC (ARMword instr)
{
  int      i;
  ARMdword t = 0;
  ARMword  a, b;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wmac\n");
#endif  

  for (i = 0; i < 4; i++)
    {
      if (BIT (21))
        {
	  /* Signed.  */
	  signed long s;

	  a = wRHALF (BITS (16, 19), i);
	  a = EXTEND16 (a);

	  b = wRHALF (BITS (0, 3), i);
	  b = EXTEND16 (b);

	  s = (signed long) a * (signed long) b;

	  t = t + (ARMdword) s;
        }
      else
        {
	  /* Unsigned.  */
	  a = wRHALF (BITS (16, 19), i);
	  b = wRHALF (BITS ( 0,  3), i);

	  t += a * b;
        }
    }

  if (BIT (20))
    wR [BITS (12, 15)] = 0;

  if (BIT (21))	/* Signed.  */
    wR[BITS (12, 15)] += t;
  else
    wR [BITS (12, 15)] += t;

  wC [wCon] |= WCON_MUP;

  return ARMul_DONE;
}

static int
WMADD (ARMword instr)
{
  ARMdword r = 0;
  int i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wmadd\n");
#endif  

  for (i = 0; i < 2; i++)
    {
      ARMdword s1, s2;

      if (BIT (21))	/* Signed.  */
        {
	  signed long a, b;

	  a = wRHALF (BITS (16, 19), i * 2);
	  a = EXTEND16 (a);

	  b = wRHALF (BITS (0, 3), i * 2);
	  b = EXTEND16 (b);

	  s1 = (ARMdword) (a * b);

	  a = wRHALF (BITS (16, 19), i * 2 + 1);
	  a = EXTEND16 (a);

	  b = wRHALF (BITS (0, 3), i * 2 + 1);
	  b = EXTEND16 (b);

	  s2 = (ARMdword) (a * b);
        }
      else			/* Unsigned.  */
        {
	  unsigned long a, b;

	  a = wRHALF (BITS (16, 19), i * 2);
	  b = wRHALF (BITS ( 0,  3), i * 2);

	  s1 = (ARMdword) (a * b);

	  a = wRHALF (BITS (16, 19), i * 2 + 1);
	  b = wRHALF (BITS ( 0,  3), i * 2 + 1);

	  s2 = (ARMdword) a * b;
        }

      r |= (ARMdword) ((s1 + s2) & 0xffffffff) << (i ? 32 : 0);
    }

  wR [BITS (12, 15)] = r;
  wC [wCon] |= WCON_MUP;

  return ARMul_DONE;
}

static int
WMAX (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMdword s;
  int      i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wmax\n");
#endif  

  switch (BITS (22, 23))
    {
    case Bqual:
      for (i = 0; i < 8; i++)
	if (BIT (21))	/* Signed.  */
	  {
	    int a, b;

	    a = wRBYTE (BITS (16, 19), i);
	    a = EXTEND8 (a);

	    b = wRBYTE (BITS (0, 3), i);
	    b = EXTEND8 (b);

	    if (a > b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xff) << (i * 8);
	  }
	else	 	/* Unsigned.  */
	  {
	    unsigned int a, b;

	    a = wRBYTE (BITS (16, 19), i);
	    b = wRBYTE (BITS (0, 3), i);

	    if (a > b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xff) << (i * 8);
          }
      break;

    case Hqual:
      for (i = 0; i < 4; i++)
	if (BIT (21))	/* Signed.  */
	  {
	    int a, b;

	    a = wRHALF (BITS (16, 19), i);
	    a = EXTEND16 (a);

	    b = wRHALF (BITS (0, 3), i);
	    b = EXTEND16 (b);

	    if (a > b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xffff) << (i * 16);
	  }
	else	 	/* Unsigned.  */
	  {
	    unsigned int a, b;

	    a = wRHALF (BITS (16, 19), i);
	    b = wRHALF (BITS (0, 3), i);

	    if (a > b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xffff) << (i * 16);
          }
      break;

    case Wqual:
      for (i = 0; i < 2; i++)
	if (BIT (21))	/* Signed.  */
	  {
	    int a, b;

	    a = wRWORD (BITS (16, 19), i);
	    b = wRWORD (BITS (0, 3), i);

	    if (a > b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xffffffff) << (i * 32);
	  }
	else
	  {
	    unsigned int a, b;

	    a = wRWORD (BITS (16, 19), i);
	    b = wRWORD (BITS (0, 3), i);

	    if (a > b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xffffffff) << (i * 32);
          }
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wR [BITS (12, 15)] = r;
  wC [wCon] |= WCON_MUP;

  return ARMul_DONE;
}

static int
WMIN (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMdword s;
  int      i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wmin\n");
#endif  

  switch (BITS (22, 23))
    {
    case Bqual:
      for (i = 0; i < 8; i++)
	if (BIT (21))	/* Signed.  */
	  {
	    int a, b;

	    a = wRBYTE (BITS (16, 19), i);
	    a = EXTEND8 (a);

	    b = wRBYTE (BITS (0, 3), i);
	    b = EXTEND8 (b);

	    if (a < b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xff) << (i * 8);
	  }
	else	 	/* Unsigned.  */
	  {
	    unsigned int a, b;

	    a = wRBYTE (BITS (16, 19), i);
	    b = wRBYTE (BITS (0, 3), i);

	    if (a < b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xff) << (i * 8);
          }
      break;

    case Hqual:
      for (i = 0; i < 4; i++)
	if (BIT (21))	/* Signed.  */
	  {
	    int a, b;

	    a = wRHALF (BITS (16, 19), i);
	    a = EXTEND16 (a);

	    b = wRHALF (BITS (0, 3), i);
	    b = EXTEND16 (b);

	    if (a < b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xffff) << (i * 16);
	  }
	else
	  {
	    /* Unsigned.  */
	    unsigned int a, b;

	    a = wRHALF (BITS (16, 19), i);
	    b = wRHALF (BITS ( 0,  3), i);

	    if (a < b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xffff) << (i * 16);
          }
      break;

    case Wqual:
      for (i = 0; i < 2; i++)
	if (BIT (21))	/* Signed.  */
	  {
	    int a, b;

	    a = wRWORD (BITS (16, 19), i);
	    b = wRWORD (BITS ( 0,  3), i);

	    if (a < b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xffffffff) << (i * 32);
	  }
	else
	  {
	    unsigned int a, b;

	    a = wRWORD (BITS (16, 19), i);
	    b = wRWORD (BITS (0, 3), i);

	    if (a < b)
	      s = a;
	    else
	      s = b;

	    r |= (s & 0xffffffff) << (i * 32);
          }
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wR [BITS (12, 15)] = r;
  wC [wCon] |= WCON_MUP;
  
  return ARMul_DONE;
}

static int
WMUL (ARMword instr)
{
  ARMdword r = 0;
  ARMdword s;
  int      i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wmul\n");
#endif  

  for (i = 0; i < 4; i++)
    if (BIT (21))	/* Signed.  */
      {
	long a, b;

	a = wRHALF (BITS (16, 19), i);
	a = EXTEND16 (a);

	b = wRHALF (BITS (0, 3), i);
	b = EXTEND16 (b);

	s = a * b;

	if (BIT (20))
	  r |= ((s >> 16) & 0xffff) << (i * 16);
	else
	  r |= (s & 0xffff) << (i * 16);
      }
    else		/* Unsigned.  */
      {
	unsigned long a, b;

	a = wRHALF (BITS (16, 19), i);
	b = wRHALF (BITS (0, 3), i);

	s = a * b;

	if (BIT (20))
	  r |= ((s >> 16) & 0xffff) << (i * 16);
	else
	  r |= (s & 0xffff) << (i * 16);
      }

  wR [BITS (12, 15)] = r;
  wC [wCon] |= WCON_MUP;

  return ARMul_DONE;
}

static int
WOR (ARMword instr)
{
  ARMword psr = 0;
  ARMdword result;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wor\n");
#endif  

  result = wR [BITS (16, 19)] | wR [BITS (0, 3)];
  wR [BITS (12, 15)] = result;

  SIMD64_SET (psr, (result == 0), SIMD_ZBIT);
  SIMD64_SET (psr, (result & (1ULL << 63)), SIMD_NBIT);
  
  wC [wCASF] = psr;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WPACK (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMword  psr = 0;
  ARMdword x;
  ARMdword s;
  int      i;
  int      satrv[8];

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wpack\n");
#endif  
 
  switch (BITS (22, 23))
    {
    case Hqual:
      for (i = 0; i < 8; i++)
	{
	  x = wRHALF (i < 4 ? BITS (16, 19) : BITS (0, 3), i & 3);

	  switch (BITS (20, 21))
	    {
	    case UnsignedSaturation:
	      s = IwmmxtSaturateU8 (x, satrv + BITIDX8 (i));
	      break;

	    case SignedSaturation:
	      s = IwmmxtSaturateS8 (x, satrv + BITIDX8 (i));
	      break;

	    default:
	      ARMul_UndefInstr (state, instr);
	      return ARMul_DONE;
	    }

	  r |= (s & 0xff) << (i * 8);
	  SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i);
	  SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i);
	}
      break;

    case Wqual:
      satrv[0] = satrv[2] = satrv[4] = satrv[6] = 0;

      for (i = 0; i < 4; i++)
	{
	  x = wRWORD (i < 2 ? BITS (16, 19) : BITS (0, 3), i & 1);

	  switch (BITS (20, 21))
	    {
	    case UnsignedSaturation:
	      s = IwmmxtSaturateU16 (x, satrv + BITIDX16 (i));
	      break;

	    case SignedSaturation:
	      s = IwmmxtSaturateS16 (x, satrv + BITIDX16 (i));
	      break;

	    default:
	      ARMul_UndefInstr (state, instr);
	      return ARMul_DONE;
	    }

	  r |= (s & 0xffff) << (i * 16);
	  SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	  SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	}
      break;

    case Dqual:
      satrv[0] = satrv[1] = satrv[2] = satrv[4] = satrv[5] = satrv[6] = 0;

      for (i = 0; i < 2; i++)
	{
	  x = wR [i ? BITS (0, 3) : BITS (16, 19)];

	  switch (BITS (20, 21))
	    {
	    case UnsignedSaturation:
	      s = IwmmxtSaturateU32 (x, satrv + BITIDX32 (i));
	      break;

	    case SignedSaturation:
	      s = IwmmxtSaturateS32 (x, satrv + BITIDX32 (i));
	      break;

	    default:
	      ARMul_UndefInstr (state, instr);
	      return ARMul_DONE;
	    }

	  r |= (s & 0xffffffff) << (i * 32);
	  SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	  SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	}
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  SET_wCSSFvec (satrv);
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WROR (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMdword s;
  ARMword  psr = 0;
  int      i;
  int      shift;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wror\n");
#endif  

  DECODE_G_BIT (state, instr, shift);

  switch (BITS (22, 23))
    {
    case Hqual:
      shift &= 0xf;
      for (i = 0; i < 4; i++)
	{
	  s = ((wRHALF (BITS (16, 19), i) & 0xffff) << (16 - shift))
	    | ((wRHALF (BITS (16, 19), i) & 0xffff) >> shift);
	  r |= (s & 0xffff) << (i * 16);
	  SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	  SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	}
      break;

    case Wqual:
      shift &= 0x1f;
      for (i = 0; i < 2; i++)
	{
	  s = ((wRWORD (BITS (16, 19), i) & 0xffffffff) << (32 - shift))
	    | ((wRWORD (BITS (16, 19), i) & 0xffffffff) >> shift);
	  r |= (s & 0xffffffff) << (i * 32);
	  SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	  SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	}
      break;

    case Dqual:
      shift &= 0x3f;
      r = (wR [BITS (16, 19)] >> shift)
	| (wR [BITS (16, 19)] << (64 - shift));

      SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT);
      SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WSAD (ARMword instr)
{
  ARMdword r;
  int      s;
  int      i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wsad\n");
#endif  

  /* Z bit.  */
  r = BIT (20) ? 0 : (wR [BITS (12, 15)] & 0xffffffff);

  if (BIT (22))
    /* Half.  */
    for (i = 0; i < 4; i++)
      {
	s = (wRHALF (BITS (16, 19), i) - wRHALF (BITS (0, 3), i));
	r += abs (s);
      }
  else
    /* Byte.  */
    for (i = 0; i < 8; i++)
      {
	s = (wRBYTE (BITS (16, 19), i) - wRBYTE (BITS (0, 3), i));
	r += abs (s);
      }

  wR [BITS (12, 15)] = r;
  wC [wCon] |= WCON_MUP;

  return ARMul_DONE;
}

static int
WSHUFH (ARMword instr)
{
  ARMdword r = 0;
  ARMword  psr = 0;
  ARMdword s;
  int      i;
  int      imm8;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wshufh\n");
#endif  

  imm8 = (BITS (20, 23) << 4) | BITS (0, 3);

  for (i = 0; i < 4; i++)
    {
      s = wRHALF (BITS (16, 19), ((imm8 >> (i * 2) & 3)) & 0xff);
      r |= (s & 0xffff) << (i * 16);
      SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
      SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WSLL (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMdword s;
  ARMword  psr = 0;
  int      i;
  unsigned shift;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wsll\n");
#endif  

  DECODE_G_BIT (state, instr, shift);

  switch (BITS (22, 23))
    {
    case Hqual:
      for (i = 0; i < 4; i++)
	{
	  if (shift > 15)
	    s = 0;
	  else
	    s = ((wRHALF (BITS (16, 19), i) & 0xffff) << shift);
	  r |= (s & 0xffff) << (i * 16);
	  SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	  SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	}
      break;

    case Wqual:
      for (i = 0; i < 2; i++)
	{
	  if (shift > 31)
	    s = 0;
	  else
	    s = ((wRWORD (BITS (16, 19), i) & 0xffffffff) << shift);
	  r |= (s & 0xffffffff) << (i * 32);
	  SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	  SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	}
      break;

    case Dqual:
      if (shift > 63)
	r = 0;
      else
	r = ((wR[BITS (16, 19)] & 0xffffffffffffffffULL) << shift);

      SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT);
      SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WSRA (ARMul_State * state, ARMword instr)
{
  ARMdword     r = 0;
  ARMdword     s;
  ARMword      psr = 0;
  int          i;
  unsigned     shift;
  signed long  t;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wsra\n");
#endif  

  DECODE_G_BIT (state, instr, shift);

  switch (BITS (22, 23))
    {
    case Hqual:
      for (i = 0; i < 4; i++)
	{
	  if (shift > 15)
	    t = (wRHALF (BITS (16, 19), i) & 0x8000) ? 0xffff : 0;
	  else
	    {
	      t = wRHALF (BITS (16, 19), i);
	      t = EXTEND16 (t);
	      t >>= shift;
	    }

	  s = t;
	  r |= (s & 0xffff) << (i * 16);
	  SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	  SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	}
      break;

    case Wqual:
      for (i = 0; i < 2; i++)
	{
	  if (shift > 31)
	    t = (wRWORD (BITS (16, 19), i) & 0x80000000) ? 0xffffffff : 0;
	  else
	    {
	      t = wRWORD (BITS (16, 19), i);
	      t >>= shift;
	    }
	  s = t;
	  r |= (s & 0xffffffff) << (i * 32);
	  SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	  SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	}
      break;
      
    case Dqual:
      if (shift > 63)
	r = (wR [BITS (16, 19)] & 0x8000000000000000ULL) ? 0xffffffffffffffffULL : 0;
      else
	r = ((signed long long) (wR[BITS (16, 19)] & 0xffffffffffffffffULL) >> shift);
      SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT);
      SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WSRL (ARMul_State * state, ARMword instr)
{
  ARMdword     r = 0;
  ARMdword     s;
  ARMword      psr = 0;
  int          i;
  unsigned int shift;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wsrl\n");
#endif

  DECODE_G_BIT (state, instr, shift);

  switch (BITS (22, 23))
    {
    case Hqual:
      for (i = 0; i < 4; i++)
	{
	  if (shift > 15)
	    s = 0;
	  else
	    s = ((unsigned) (wRHALF (BITS (16, 19), i) & 0xffff) >> shift);

	  r |= (s & 0xffff) << (i * 16);
	  SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	  SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	}
      break;

    case Wqual:
      for (i = 0; i < 2; i++)
	{
	  if (shift > 31)
	    s = 0;
	  else
	    s = ((unsigned long) (wRWORD (BITS (16, 19), i) & 0xffffffff) >> shift);

	  r |= (s & 0xffffffff) << (i * 32);
	  SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	  SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	}
      break;

    case Dqual:
      if (shift > 63)
	r = 0;
      else
	r = (wR [BITS (16, 19)] & 0xffffffffffffffffULL) >> shift;

      SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT);
      SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WSTR (ARMul_State * state, ARMword instr)
{
  ARMword address;
  int failed;


  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wstr\n");
#endif
  
  address = Compute_Iwmmxt_Address (state, instr, & failed);
  if (failed)
    return ARMul_CANT;

  if (BITS (28, 31) == 0xf)
    {
      /* WSTRW wCx */
      Iwmmxt_Store_Word (state, address, wC [BITS (12, 15)]);
    }
  else if (BIT (8) == 0)
    {
      if (BIT (22) == 0)
	/* WSTRB */
	Iwmmxt_Store_Byte (state, address, wR [BITS (12, 15)]);
      else
	/* WSTRH */
	Iwmmxt_Store_Half_Word (state, address, wR [BITS (12, 15)]);
    }
  else
    {
      if (BIT (22) == 0)
	/* WSTRW wRd */
	Iwmmxt_Store_Word (state, address, wR [BITS (12, 15)]);
      else
	/* WSTRD */
	Iwmmxt_Store_Double_Word (state, address, wR [BITS (12, 15)]);
    }

  return ARMul_DONE;
}

static int
WSUB (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMword  psr = 0;
  ARMdword x;
  ARMdword s;
  int      i;
  int      carry;
  int      overflow;
  int      satrv[8];

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wsub\n");
#endif  

/* Subtract two numbers using the specified function,
   leaving setting the carry bit as required.  */
#define SUBx(x, y, m, f) \
   (*f) (wRBITS (BITS (16, 19), (x), (y)) & (m), \
         wRBITS (BITS ( 0,  3), (x), (y)) & (m), & carry, & overflow)

  switch (BITS (22, 23))
    {
    case Bqual:
      for (i = 0; i < 8; i++)
        {
	  switch (BITS (20, 21))
	    {
	    case NoSaturation:
	      s = SUBx ((i * 8), (i * 8) + 7, 0xff, SubS8);
	      satrv [BITIDX8 (i)] = 0;
	      r |= (s & 0xff) << (i * 8);
	      SIMD8_SET (psr, NBIT8 (s), SIMD_NBIT, i);
	      SIMD8_SET (psr, ZBIT8 (s), SIMD_ZBIT, i);
	      SIMD8_SET (psr, carry, SIMD_CBIT, i);
	      SIMD8_SET (psr, overflow, SIMD_VBIT, i);
	      break;

	    case UnsignedSaturation:
	      s = SUBx ((i * 8), (i * 8) + 7, 0xff, SubU8);
	      x = IwmmxtSaturateU8 (s, satrv + BITIDX8 (i));
	      r |= (x & 0xff) << (i * 8);
	      SIMD8_SET (psr, NBIT8 (x), SIMD_NBIT, i);
	      SIMD8_SET (psr, ZBIT8 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX8 (i)])
		{
		  SIMD8_SET (psr, carry,     SIMD_CBIT, i);
		  SIMD8_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    case SignedSaturation:
	      s = SUBx ((i * 8), (i * 8) + 7, 0xff, SubS8);
	      x = IwmmxtSaturateS8 (s, satrv + BITIDX8 (i));
	      r |= (x & 0xff) << (i * 8);
	      SIMD8_SET (psr, NBIT8 (x), SIMD_NBIT, i);
	      SIMD8_SET (psr, ZBIT8 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX8 (i)])
		{
		  SIMD8_SET (psr, carry,     SIMD_CBIT, i);
		  SIMD8_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    default:
	      ARMul_UndefInstr (state, instr);
	      return ARMul_DONE;
	    }
	}
      break;

    case Hqual:
      satrv[0] = satrv[2] = satrv[4] = satrv[6] = 0;

      for (i = 0; i < 4; i++)
	{
	  switch (BITS (20, 21))
	    {
	    case NoSaturation:
	      s = SUBx ((i * 16), (i * 16) + 15, 0xffff, SubU16);
	      satrv [BITIDX16 (i)] = 0;
	      r |= (s & 0xffff) << (i * 16);
	      SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	      SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	      SIMD16_SET (psr, carry,      SIMD_CBIT, i);
	      SIMD16_SET (psr, overflow,   SIMD_VBIT, i);
	      break;

	    case UnsignedSaturation:
	      s = SUBx ((i * 16), (i * 16) + 15, 0xffff, SubU16);
	      x = IwmmxtSaturateU16 (s, satrv + BITIDX16 (i));
	      r |= (x & 0xffff) << (i * 16);
	      SIMD16_SET (psr, NBIT16 (x & 0xffff), SIMD_NBIT, i);
	      SIMD16_SET (psr, ZBIT16 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX16 (i)])
		{
		  SIMD16_SET (psr, carry,    SIMD_CBIT, i);
		  SIMD16_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    case SignedSaturation:
	      s = SUBx ((i * 16), (i * 16) + 15, 0xffff, SubS16);
	      x = IwmmxtSaturateS16 (s, satrv + BITIDX16 (i));
	      r |= (x & 0xffff) << (i * 16);
	      SIMD16_SET (psr, NBIT16 (x), SIMD_NBIT, i);
	      SIMD16_SET (psr, ZBIT16 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX16 (i)])
		{
		  SIMD16_SET (psr, carry,    SIMD_CBIT, i);
		  SIMD16_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    default:
	      ARMul_UndefInstr (state, instr);
	      return ARMul_DONE;
	    }
	}
      break;

    case Wqual:
      satrv[0] = satrv[1] = satrv[2] = satrv[4] = satrv[5] = satrv[6] = 0;

      for (i = 0; i < 2; i++)
	{
	  switch (BITS (20, 21))
	    {
	    case NoSaturation:
	      s = SUBx ((i * 32), (i * 32) + 31, 0xffffffff, SubU32);
	      satrv[BITIDX32 (i)] = 0;
	      r |= (s & 0xffffffff) << (i * 32);
	      SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	      SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	      SIMD32_SET (psr, carry,      SIMD_CBIT, i);
	      SIMD32_SET (psr, overflow,   SIMD_VBIT, i);
	      break;

	    case UnsignedSaturation:
	      s = SUBx ((i * 32), (i * 32) + 31, 0xffffffff, SubU32);
	      x = IwmmxtSaturateU32 (s, satrv + BITIDX32 (i));
	      r |= (x & 0xffffffff) << (i * 32);
	      SIMD32_SET (psr, NBIT32 (x), SIMD_NBIT, i);
	      SIMD32_SET (psr, ZBIT32 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX32 (i)])
		{
		  SIMD32_SET (psr, carry,    SIMD_CBIT, i);
		  SIMD32_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    case SignedSaturation:
	      s = SUBx ((i * 32), (i * 32) + 31, 0xffffffff, SubS32);
	      x = IwmmxtSaturateS32 (s, satrv + BITIDX32 (i));
	      r |= (x & 0xffffffff) << (i * 32);
	      SIMD32_SET (psr, NBIT32 (x), SIMD_NBIT, i);
	      SIMD32_SET (psr, ZBIT32 (x), SIMD_ZBIT, i);
	      if (! satrv [BITIDX32 (i)])
		{
		  SIMD32_SET (psr, carry,    SIMD_CBIT, i);
		  SIMD32_SET (psr, overflow, SIMD_VBIT, i);
		}
	      break;

	    default:
	      ARMul_UndefInstr (state, instr);
	      return ARMul_DONE;
	    }
	}
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wR [BITS (12, 15)] = r;
  wC [wCASF] = psr;
  SET_wCSSFvec (satrv);
  wC [wCon] |= (WCON_CUP | WCON_MUP);

#undef SUBx

  return ARMul_DONE;
}

static int
WUNPCKEH (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMword  psr = 0;
  ARMdword s;
  int      i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wunpckeh\n");
#endif  

  switch (BITS (22, 23))
    {
    case Bqual:
      for (i = 0; i < 4; i++)
	{
	  s = wRBYTE (BITS (16, 19), i + 4);

	  if (BIT (21) && NBIT8 (s))
	    s |= 0xff00;

	  r |= (s & 0xffff) << (i * 16);
	  SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	  SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	}
      break;

    case Hqual:
      for (i = 0; i < 2; i++)
	{
	  s = wRHALF (BITS (16, 19), i + 2);

	  if (BIT (21) && NBIT16 (s))
	    s |= 0xffff0000;

	  r |= (s & 0xffffffff) << (i * 32);
	  SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	  SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	}
      break;

    case Wqual:
      r = wRWORD (BITS (16, 19), 1);

      if (BIT (21) && NBIT32 (r))
	r |= 0xffffffff00000000ULL;

      SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT);
      SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WUNPCKEL (ARMul_State * state, ARMword instr)
{
  ARMdword r = 0;
  ARMword  psr = 0;
  ARMdword s;
  int      i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wunpckel\n");
#endif  

  switch (BITS (22, 23))
    {
    case Bqual:
      for (i = 0; i < 4; i++)
	{
	  s = wRBYTE (BITS (16, 19), i);

	  if (BIT (21) && NBIT8 (s))
	    s |= 0xff00;

	  r |= (s & 0xffff) << (i * 16);
	  SIMD16_SET (psr, NBIT16 (s), SIMD_NBIT, i);
	  SIMD16_SET (psr, ZBIT16 (s), SIMD_ZBIT, i);
	}
      break;

    case Hqual:
      for (i = 0; i < 2; i++)
	{
	  s = wRHALF (BITS (16, 19), i);

	  if (BIT (21) && NBIT16 (s))
	    s |= 0xffff0000;

	  r |= (s & 0xffffffff) << (i * 32);
	  SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, i);
	  SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, i);
	}
      break;

    case Wqual:
      r = wRWORD (BITS (16, 19), 0);

      if (BIT (21) && NBIT32 (r))
	r |= 0xffffffff00000000ULL;

      SIMD64_SET (psr, NBIT64 (r), SIMD_NBIT);
      SIMD64_SET (psr, ZBIT64 (r), SIMD_ZBIT);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WUNPCKIH (ARMul_State * state, ARMword instr)
{
  ARMword  a, b;
  ARMdword r = 0;
  ARMword  psr = 0;
  ARMdword s;
  int      i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wunpckih\n");
#endif  

  switch (BITS (22, 23))
    {
    case Bqual:
      for (i = 0; i < 4; i++)
	{
	  a = wRBYTE (BITS (16, 19), i + 4);
	  b = wRBYTE (BITS ( 0,  3), i + 4);
	  s = a | (b << 8);
	  r |= (s & 0xffff) << (i * 16);
	  SIMD8_SET (psr, NBIT8 (a), SIMD_NBIT, i * 2);
	  SIMD8_SET (psr, ZBIT8 (a), SIMD_ZBIT, i * 2);
	  SIMD8_SET (psr, NBIT8 (b), SIMD_NBIT, (i * 2) + 1);
	  SIMD8_SET (psr, ZBIT8 (b), SIMD_ZBIT, (i * 2) + 1);
	}
      break;
      
    case Hqual:
      for (i = 0; i < 2; i++)
	{
	  a = wRHALF (BITS (16, 19), i + 2);
	  b = wRHALF (BITS ( 0,  3), i + 2);
	  s = a | (b << 16);
	  r |= (s & 0xffffffff) << (i * 32);
	  SIMD16_SET (psr, NBIT16 (a), SIMD_NBIT, (i * 2));
	  SIMD16_SET (psr, ZBIT16 (a), SIMD_ZBIT, (i * 2));
	  SIMD16_SET (psr, NBIT16 (b), SIMD_NBIT, (i * 2) + 1);
	  SIMD16_SET (psr, ZBIT16 (b), SIMD_ZBIT, (i * 2) + 1);
	}
      break;

    case Wqual:
      a = wRWORD (BITS (16, 19), 1);
      s = wRWORD (BITS ( 0,  3), 1);
      r = a | (s << 32);

      SIMD32_SET (psr, NBIT32 (a), SIMD_NBIT, 0);
      SIMD32_SET (psr, ZBIT32 (a), SIMD_ZBIT, 0);
      SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, 1);
      SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, 1);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WUNPCKIL (ARMul_State * state, ARMword instr)
{
  ARMword  a, b;
  ARMdword r = 0;
  ARMword  psr = 0;
  ARMdword s;
  int      i;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wunpckil\n");
#endif  

  switch (BITS (22, 23))
    {
    case Bqual:
      for (i = 0; i < 4; i++)
	{
	  a = wRBYTE (BITS (16, 19), i);
	  b = wRBYTE (BITS ( 0,  3), i);
	  s = a | (b << 8);
	  r |= (s & 0xffff) << (i * 16);
	  SIMD8_SET (psr, NBIT8 (a), SIMD_NBIT, i * 2);
	  SIMD8_SET (psr, ZBIT8 (a), SIMD_ZBIT, i * 2);
	  SIMD8_SET (psr, NBIT8 (b), SIMD_NBIT, (i * 2) + 1);
	  SIMD8_SET (psr, ZBIT8 (b), SIMD_ZBIT, (i * 2) + 1);
	}
      break;

    case Hqual:
      for (i = 0; i < 2; i++)
	{
	  a = wRHALF (BITS (16, 19), i);
	  b = wRHALF (BITS ( 0,  3), i);
	  s = a | (b << 16);
	  r |= (s & 0xffffffff) << (i * 32);
	  SIMD16_SET (psr, NBIT16 (a), SIMD_NBIT, (i * 2));
	  SIMD16_SET (psr, ZBIT16 (a), SIMD_ZBIT, (i * 2));
	  SIMD16_SET (psr, NBIT16 (b), SIMD_NBIT, (i * 2) + 1);
	  SIMD16_SET (psr, ZBIT16 (b), SIMD_ZBIT, (i * 2) + 1);
	}
      break;

    case Wqual:
      a = wRWORD (BITS (16, 19), 0);
      s = wRWORD (BITS ( 0,  3), 0);
      r = a | (s << 32);

      SIMD32_SET (psr, NBIT32 (a), SIMD_NBIT, 0);
      SIMD32_SET (psr, ZBIT32 (a), SIMD_ZBIT, 0);
      SIMD32_SET (psr, NBIT32 (s), SIMD_NBIT, 1);
      SIMD32_SET (psr, ZBIT32 (s), SIMD_ZBIT, 1);
      break;

    default:
      ARMul_UndefInstr (state, instr);
      return ARMul_DONE;
    }

  wC [wCASF] = psr;
  wR [BITS (12, 15)] = r;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

static int
WXOR (ARMword instr)
{
  ARMword psr = 0;
  ARMdword result;

  if ((read_cp15_reg (15, 0, 1) & 3) != 3)
    return ARMul_CANT;

#ifdef DEBUG
  fprintf (stderr, "wxor\n");
#endif  

  result = wR [BITS (16, 19)] ^ wR [BITS (0, 3)];
  wR [BITS (12, 15)] = result;

  SIMD64_SET (psr, (result == 0), SIMD_ZBIT);
  SIMD64_SET (psr, (result & (1ULL << 63)), SIMD_NBIT);
  
  wC [wCASF] = psr;
  wC [wCon] |= (WCON_CUP | WCON_MUP);

  return ARMul_DONE;
}

/* This switch table is moved to a seperate function in order
   to work around a compiler bug in the host compiler...  */

static int
Process_Instruction (ARMul_State * state, ARMword instr)
{
  int status = ARMul_BUSY;

  switch ((BITS (20, 23) << 8) | BITS (4, 11))
    {
    case 0x000: status = WOR (instr); break;
    case 0x011: status = TMCR (state, instr); break;
    case 0x100: status = WXOR (instr); break;
    case 0x111: status = TMRC (state, instr); break;
    case 0x300: status = WANDN (instr); break;
    case 0x200: status = WAND (instr); break;

    case 0x810: case 0xa10:
      status = WMADD (instr); break;

    case 0x10e: case 0x50e: case 0x90e: case 0xd0e:
      status = WUNPCKIL (state, instr); break;	  
    case 0x10c: case 0x50c: case 0x90c: case 0xd0c:
      status = WUNPCKIH (state, instr); break;
    case 0x012: case 0x112: case 0x412: case 0x512:
      status = WSAD (instr); break;
    case 0x010: case 0x110: case 0x210: case 0x310:
      status = WMUL (instr); break;
    case 0x410: case 0x510: case 0x610: case 0x710:
      status = WMAC (instr); break;
    case 0x006: case 0x406: case 0x806: case 0xc06:
      status = WCMPEQ (state, instr); break;
    case 0x800: case 0x900: case 0xc00: case 0xd00:
      status = WAVG2 (instr); break;
    case 0x802: case 0x902: case 0xa02: case 0xb02:
      status = WALIGNR (state, instr); break;
    case 0x601: case 0x605: case 0x609: case 0x60d:
      status = TINSR (state, instr); break;
    case 0x107: case 0x507: case 0x907: case 0xd07:
      status = TEXTRM (state, instr); break;
    case 0x117: case 0x517: case 0x917: case 0xd17:
      status = TEXTRC (state, instr); break;
    case 0x401: case 0x405: case 0x409: case 0x40d:
      status = TBCST (state, instr); break;
    case 0x113: case 0x513: case 0x913: case 0xd13:
      status = TANDC (state, instr); break;
    case 0x01c: case 0x41c: case 0x81c: case 0xc1c:
      status = WACC (state, instr); break;
    case 0x115: case 0x515: case 0x915: case 0xd15:
      status = TORC (state, instr); break;
    case 0x103: case 0x503: case 0x903: case 0xd03:
      status = TMOVMSK (state, instr); break;
    case 0x106: case 0x306: case 0x506: case 0x706:
    case 0x906: case 0xb06: case 0xd06: case 0xf06:
      status = WCMPGT (state, instr); break;
    case 0x00e: case 0x20e: case 0x40e: case 0x60e:
    case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e:
      status = WUNPCKEL (state, instr); break;
    case 0x00c: case 0x20c: case 0x40c: case 0x60c:
    case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c:
      status = WUNPCKEH (state, instr); break;
    case 0x204: case 0x604: case 0xa04: case 0xe04:
    case 0x214: case 0x614: case 0xa14: case 0xe14:
      status = WSRL (state, instr); break;
    case 0x004: case 0x404: case 0x804: case 0xc04:
    case 0x014: case 0x414: case 0x814: case 0xc14:
      status = WSRA (state, instr); break;
    case 0x104: case 0x504: case 0x904: case 0xd04:
    case 0x114: case 0x514: case 0x914: case 0xd14:
      status = WSLL (state, instr); break;
    case 0x304: case 0x704: case 0xb04: case 0xf04:
    case 0x314: case 0x714: case 0xb14: case 0xf14:
      status = WROR (state, instr); break;
    case 0x116: case 0x316: case 0x516: case 0x716:
    case 0x916: case 0xb16: case 0xd16: case 0xf16:
      status = WMIN (state, instr); break;
    case 0x016: case 0x216: case 0x416: case 0x616:
    case 0x816: case 0xa16: case 0xc16: case 0xe16:
      status = WMAX (state, instr); break;
    case 0x002: case 0x102: case 0x202: case 0x302:
    case 0x402: case 0x502: case 0x602: case 0x702:
      status = WALIGNI (instr); break;
    case 0x01a: case 0x11a: case 0x21a: case 0x31a:
    case 0x41a: case 0x51a: case 0x61a: case 0x71a:
    case 0x81a: case 0x91a: case 0xa1a: case 0xb1a:
    case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a:
      status = WSUB (state, instr); break;
    case 0x01e: case 0x11e: case 0x21e: case 0x31e:	  
    case 0x41e: case 0x51e: case 0x61e: case 0x71e:
    case 0x81e: case 0x91e: case 0xa1e: case 0xb1e:
    case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e:
      status = WSHUFH (instr); break;
    case 0x018: case 0x118: case 0x218: case 0x318:
    case 0x418: case 0x518: case 0x618: case 0x718:
    case 0x818: case 0x918: case 0xa18: case 0xb18:
    case 0xc18: case 0xd18: case 0xe18: case 0xf18:
      status = WADD (state, instr); break;
    case 0x008: case 0x108: case 0x208: case 0x308:
    case 0x408: case 0x508: case 0x608: case 0x708:
    case 0x808: case 0x908: case 0xa08: case 0xb08:
    case 0xc08: case 0xd08: case 0xe08: case 0xf08:
      status = WPACK (state, instr); break;
    case 0x201: case 0x203: case 0x205: case 0x207:
    case 0x209: case 0x20b: case 0x20d: case 0x20f:
    case 0x211: case 0x213: case 0x215: case 0x217:	  
    case 0x219: case 0x21b: case 0x21d: case 0x21f:	  
      switch (BITS (16, 19))
	{
	case 0x0: status = TMIA (state, instr); break;
	case 0x8: status = TMIAPH (state, instr); break;
	case 0xc:
	case 0xd:
	case 0xe:
	case 0xf: status = TMIAxy (state, instr); break;
	default: break;
	}
      break;
    default:
      break;
    }
  return status;
}

/* Process a possibly Intel(r) Wireless MMX(tm) technology instruction.
   Return true if the instruction was handled.  */

int
ARMul_HandleIwmmxt (ARMul_State * state, ARMword instr)
{     
  int status = ARMul_BUSY;

  if (BITS (24, 27) == 0xe)
    {
      status = Process_Instruction (state, instr);
    }
  else if (BITS (25, 27) == 0x6)
    {
      if (BITS (4, 11) == 0x0 && BITS (20, 24) == 0x4)
	status = TMCRR (state, instr);
      else if (BITS (9, 11) == 0x0)
	{
	  if (BIT (20) == 0x0)
	    status = WSTR (state, instr);
	  else if (BITS (20, 24) == 0x5)
	    status = TMRRC (state, instr);
	  else
	    status = WLDR (state, instr);
	}
    }

  if (status == ARMul_CANT)
    {
      /* If the instruction was a recognised but illegal,
	 perform the abort here rather than returning false.
	 If we return false then ARMul_MRC may be called which
	 will still abort, but which also perform the register
	 transfer...  */
      ARMul_Abort (state, ARMul_UndefinedInstrV);
      status = ARMul_DONE;
    }

  return status == ARMul_DONE;
}

int
Fetch_Iwmmxt_Register (unsigned int regnum, unsigned char * memory)
{
  if (regnum >= 16)
    {
      memcpy (memory, wC + (regnum - 16), sizeof wC [0]);
      return sizeof wC [0];
    }
  else
    {
      memcpy (memory, wR + regnum, sizeof wR [0]);
      return sizeof wR [0];
    }
}

int
Store_Iwmmxt_Register (unsigned int regnum, unsigned char * memory)
{
  if (regnum >= 16)
    {
      memcpy (wC + (regnum - 16), memory, sizeof wC [0]);
      return sizeof wC [0];
    }
  else
    {
      memcpy (wR + regnum, memory, sizeof wR [0]);
      return sizeof wR [0];
    }
}
