
/*--------------------------------------------------------------------*/
/*---                                                              ---*/
/*--- This file (guest-arm/toIR.c) is                              ---*/
/*--- Copyright (C) OpenWorks LLP.  All rights reserved.           ---*/
/*---                                                              ---*/
/*--------------------------------------------------------------------*/

/*
   This file is part of LibVEX, a library for dynamic binary
   instrumentation and translation.

   Copyright (C) 2004-2006 OpenWorks LLP.  All rights reserved.

   This library is made available under a dual licensing scheme.

   If you link LibVEX against other code all of which is itself
   licensed under the GNU General Public License, version 2 dated June
   1991 ("GPL v2"), then you may use LibVEX under the terms of the GPL
   v2, as appearing in the file LICENSE.GPL.  If the file LICENSE.GPL
   is missing, you can obtain a copy of the GPL v2 from the Free
   Software Foundation Inc., 51 Franklin St, Fifth Floor, Boston, MA
   02110-1301, USA.

   For any other uses of LibVEX, you must first obtain a commercial
   license from OpenWorks LLP.  Please contact info@open-works.co.uk
   for information about commercial licensing.

   This software is provided by OpenWorks LLP "as is" and any express
   or implied warranties, including, but not limited to, the implied
   warranties of merchantability and fitness for a particular purpose
   are disclaimed.  In no event shall OpenWorks LLP be liable for any
   direct, indirect, incidental, special, exemplary, or consequential
   damages (including, but not limited to, procurement of substitute
   goods or services; loss of use, data, or profits; or business
   interruption) however caused and on any theory of liability,
   whether in contract, strict liability, or tort (including
   negligence or otherwise) arising in any way out of the use of this
   software, even if advised of the possibility of such damage.

   Neither the names of the U.S. Department of Energy nor the
   University of California nor the names of its contributors may be
   used to endorse or promote products derived from this software
   without prior written permission.
*/

/* Translates ARM(v4) code to IR. */

#include "libvex_basictypes.h"
#include "libvex_ir.h"
#include "libvex.h"
#include "libvex_guest_arm.h"

#include "main/vex_util.h"
#include "main/vex_globals.h"
#include "guest-arm/gdefs.h"


/*------------------------------------------------------------*/
/*--- Globals                                              ---*/
/*------------------------------------------------------------*/

/* These are set at the start of the translation of a BB, so that we
   don't have to pass them around endlessly.  CONST means does not
   change during translation of a bb. 
*/

/* We need to know this to do sub-register accesses correctly. */
/* CONST */
static Bool host_is_bigendian;

/* Pointer to the guest code area. */
/* CONST */
static UChar* guest_code;

/* The guest address corresponding to guest_code[0]. */
/* CONST */
static Addr32 guest_pc_bbstart;

/* The IRBB* into which we're generating code. */
static IRBB* irbb;


/*------------------------------------------------------------*/
/*--- Debugging output                                     ---*/
/*------------------------------------------------------------*/

#define DIP(format, args...)           \
   if (vex_traceflags & VEX_TRACE_FE)  \
      vex_printf(format, ## args)

#define DIS(buf, format, args...)      \
   if (vex_traceflags & VEX_TRACE_FE)  \
      vex_sprintf(buf, format, ## args)




/*------------------------------------------------------------*/
/*--- Offsets of various parts of the arm guest state.     ---*/
/*------------------------------------------------------------*/

#define OFFB_R0       offsetof(VexGuestARMState,guest_R0)
#define OFFB_R1       offsetof(VexGuestARMState,guest_R1)
#define OFFB_R2       offsetof(VexGuestARMState,guest_R2)
#define OFFB_R3       offsetof(VexGuestARMState,guest_R3)
#define OFFB_R4       offsetof(VexGuestARMState,guest_R4)
#define OFFB_R5       offsetof(VexGuestARMState,guest_R5)
#define OFFB_R6       offsetof(VexGuestARMState,guest_R6)
#define OFFB_R7       offsetof(VexGuestARMState,guest_R7)
#define OFFB_R8       offsetof(VexGuestARMState,guest_R8)
#define OFFB_R9       offsetof(VexGuestARMState,guest_R9)
#define OFFB_R10      offsetof(VexGuestARMState,guest_R10)
#define OFFB_R11      offsetof(VexGuestARMState,guest_R11)
#define OFFB_R12      offsetof(VexGuestARMState,guest_R12)
#define OFFB_R13      offsetof(VexGuestARMState,guest_R13)
#define OFFB_R14      offsetof(VexGuestARMState,guest_R14)
#define OFFB_R15      offsetof(VexGuestARMState,guest_R15)

// CAB: ? guest_SYSCALLNO;

#define OFFB_CC_OP    offsetof(VexGuestARMState,guest_CC_OP)
#define OFFB_CC_DEP1  offsetof(VexGuestARMState,guest_CC_DEP1)
#define OFFB_CC_DEP2  offsetof(VexGuestARMState,guest_CC_DEP2)

// CAB: ? guest_EMWARN;


/*------------------------------------------------------------*/
/*--- Disassemble an entire basic block                    ---*/
/*------------------------------------------------------------*/

/* The results of disassembling an instruction.  There are three
   possible outcomes.  For Dis_Resteer, the disassembler _must_
   continue at the specified address.  For Dis_StopHere, the
   disassembler _must_ terminate the BB.  For Dis_Continue, we may at
   our option either disassemble the next insn, or terminate the BB;
   but in the latter case we must set the bb's ->next field to point
   to the next instruction.  */

typedef
   enum { 
      Dis_StopHere, /* this insn terminates the BB; we must stop. */
      Dis_Continue, /* we can optionally continue into the next insn */
      Dis_Resteer   /* followed a branch; continue at the spec'd addr */
   }
   DisResult;


/* forward decls .. */
static IRExpr* mkU32 ( UInt i );
static void stmt ( IRStmt* st );


/* disInstr disassembles an instruction located at &guest_code[delta],
   and sets *size to its size.  If the returned value is Dis_Resteer,
   the next guest address is assigned to *whereNext.  disInstr is not
   permitted to return Dis_Resteer if either (1) resteerOK is False,
   or (2) resteerOkFn, when applied to the address which it wishes to
   resteer into, returns False.  */
   
static DisResult disInstr ( /*IN*/  Bool    resteerOK,
                            /*IN*/  Bool    (*resteerOkFn) ( Addr64 ),
                            /*IN*/  Long    delta, 
                            /*OUT*/ Int*    size,
                            /*OUT*/ Addr64* whereNext );


/* This is the main (only, in fact) entry point for this module. */

/* Disassemble a complete basic block, starting at guest_pc_start, and
   dumping the IR into global irbb.  Returns the size, in bytes, of
   the basic block.  
*/
IRBB* bbToIR_ARM ( UChar*           armCode, 
                   Addr64           guest_pc_start, 
                   VexGuestExtents* vge,
                   Bool             (*byte_accessible)(Addr64),
                   Bool             (*chase_into_ok)(Addr64),
                   Bool             host_bigendian,
                   VexArchInfo*     archinfo_guest )
{
   Long       delta;
   Int        i, n_instrs, size, first_stmt_idx;
   Addr64     guest_next;
   Bool       resteerOK;
   DisResult  dres;
   static Int n_resteers = 0;
   Int        d_resteers = 0;

   /* check sanity .. */
   vassert(vex_control.guest_max_insns >= 1);
   vassert(vex_control.guest_max_insns < 500);
   vassert(vex_control.guest_chase_thresh >= 0);
   vassert(vex_control.guest_chase_thresh < vex_control.guest_max_insns);

   vassert(archinfo_guest->hwcaps == 0);

   /* Start a new, empty extent. */
   vge->n_used  = 1;
   vge->base[0] = guest_pc_start;
   vge->len[0]  = 0;

   /* Set up globals. */
   host_is_bigendian = host_bigendian;
   guest_code        = armCode;
   guest_pc_bbstart  = (Addr32)guest_pc_start;
   irbb              = emptyIRBB();

   vassert((guest_pc_start >> 32) == 0);

   /* Delta keeps track of how far along the armCode array we
      have so far gone. */
   delta             = 0;
   n_instrs          = 0;

   while (True) {
      vassert(n_instrs < vex_control.guest_max_insns);

      guest_next = 0;
      resteerOK = toBool(n_instrs < vex_control.guest_chase_thresh);
      first_stmt_idx = irbb->stmts_used;

      if (n_instrs > 0) {
         /* for the first insn, the dispatch loop will have set
            R15, but for all the others we have to do it ourselves. */
         stmt( IRStmt_Put( OFFB_R15, mkU32(toUInt(guest_pc_bbstart + delta))) );
      }

      dres = disInstr( resteerOK, chase_into_ok, 
                       delta, &size, &guest_next );

      /* Print the resulting IR, if needed. */
      if (vex_traceflags & VEX_TRACE_FE) {
         for (i = first_stmt_idx; i < irbb->stmts_used; i++) {
            vex_printf("              ");
            ppIRStmt(irbb->stmts[i]);
            vex_printf("\n");
         }
      }
   
      if (dres == Dis_StopHere) {
         vassert(irbb->next != NULL);
         if (vex_traceflags & VEX_TRACE_FE) {
            vex_printf("              ");
            vex_printf( "goto {");
            ppIRJumpKind(irbb->jumpkind);
            vex_printf( "} ");
            ppIRExpr( irbb->next );
            vex_printf( "\n");
         }
      }

      delta += size;
      vge->len[vge->n_used-1] = toUShort(vge->len[vge->n_used-1] + size);
      n_instrs++;
      DIP("\n");

      vassert(size > 0 && size <= 18);
      if (!resteerOK) 
         vassert(dres != Dis_Resteer);
      if (dres != Dis_Resteer) 
         vassert(guest_next == 0);

      switch (dres) {
      case Dis_Continue:
         vassert(irbb->next == NULL);
         if (n_instrs < vex_control.guest_max_insns) {
            /* keep going */
         } else {
            irbb->next = mkU32(toUInt(guest_pc_start+delta));
            return irbb;
         }
         break;
      case Dis_StopHere:
         vassert(irbb->next != NULL);
         return irbb;
      case Dis_Resteer:
         vpanic("bbToIR_ARM: Dis_Resteer: fixme");
         /* need to add code here to start a new extent ... */
         vassert(irbb->next == NULL);
         /* figure out a new delta to continue at. */
         vassert(chase_into_ok(guest_next));
         delta = guest_next - guest_pc_start;
         n_resteers++;
         d_resteers++;
         if (0 && (n_resteers & 0xFF) == 0)
            vex_printf("resteer[%d,%d] to %p (delta = %lld)\n",
                       n_resteers, d_resteers,
                       ULong_to_Ptr(guest_next), delta);
         break;
      }
   }
}


/*------------------------------------------------------------*/
/*--- Helper bits and pieces for deconstructing the        ---*/
/*--- ARM insn stream.                                     ---*/
/*------------------------------------------------------------*/

/* Add a statement to the list held by "irbb". */
static void stmt ( IRStmt* st )
{
   addStmtToIRBB( irbb, st );
}

/* Generate a new temporary of the given type. */
static IRTemp newTemp ( IRType ty )
{
   vassert(isPlausibleIRType(ty));
   return newIRTemp( irbb->tyenv, ty );
}

#if 0
/* Bomb out if we can't handle something. */
__attribute__ ((noreturn))
static void unimplemented ( Char* str )
{
   vex_printf("armToIR: unimplemented feature\n");
   vpanic(str);
}
#endif

/* Various simple conversions */

#if 0
static UInt extend_s_8to32( UInt x )
{
   return (UInt)((((Int)x) << 24) >> 24);
}

static UInt extend_s_16to32 ( UInt x )
{
   return (UInt)((((Int)x) << 16) >> 16);
}
#endif

static UInt extend_s_24to32 ( UInt x )
{
   return (UInt)((((Int)x) << 8) >> 8);
}

#if 0
/* Fetch a byte from the guest insn stream. */
static UChar getIByte ( UInt delta )
{
   return guest_code[delta];
}
#endif

/* Get a 8/16/32-bit unsigned value out of the insn stream. */

#if 0
static UInt getUChar ( UInt delta )
{
   UInt v = guest_code[delta+0];
   return v & 0xFF;
}
#endif

#if 0
static UInt getUDisp16 ( UInt delta )
{
   UInt v = guest_code[delta+1]; v <<= 8;
   v |= guest_code[delta+0];
   return v & 0xFFFF;
}
#endif

#if 0
static UInt getUDisp32 ( UInt delta )
{
   UInt v = guest_code[delta+3]; v <<= 8;
   v |= guest_code[delta+2]; v <<= 8;
   v |= guest_code[delta+1]; v <<= 8;
   v |= guest_code[delta+0];
   return v;
}
#endif

#if 0
static UInt getUDisp ( Int size, UInt delta )
{
   switch (size) {
   case 4: return getUDisp32(delta);
   case 2: return getUDisp16(delta);
   case 1: return getUChar(delta);
   default: vpanic("getUDisp(ARM)");
   }
   return 0; /*notreached*/
}
#endif

#if 0
/* Get a byte value out of the insn stream and sign-extend to 32
   bits. */
static UInt getSDisp8 ( UInt delta )
{
   return extend_s_8to32( (UInt) (guest_code[delta]) );
}
#endif

#if 0
static UInt getSDisp16 ( UInt delta0 )
{
   UChar* eip = (UChar*)(&guest_code[delta0]);
   UInt d = *eip++;
   d |= ((*eip++) << 8);
   return extend_s_16to32(d);
}
#endif

#if 0
static UInt getSDisp ( Int size, UInt delta )
{
   switch (size) {
   case 4: return getUDisp32(delta);
   case 2: return getSDisp16(delta);
   case 1: return getSDisp8(delta);
   default: vpanic("getSDisp(ARM)");
   }
   return 0; /*notreached*/
}
#endif


/*------------------------------------------------------------*/
/*--- Helpers for constructing IR.                         ---*/
/*------------------------------------------------------------*/

/* Create a 1/2/4 byte read of an x86 integer registers.  For 16/8 bit
   register references, we need to take the host endianness into
   account.  Supplied value is 0 .. 7 and in the Intel instruction
   encoding. */

#if 0
static IRType szToITy ( Int n )
{
   switch (n) {
   case 1: return Ity_I8;
   case 2: return Ity_I16;
   case 4: return Ity_I32;
   default: vpanic("szToITy(ARM)");
   }
}
#endif

static Int integerGuestRegOffset ( UInt archreg )
{
   vassert(archreg < 16);

   vassert(!host_is_bigendian);   //TODO: is this necessary?
   // jrs: probably not; only matters if we reference sub-parts
   // of the arm registers, but that isn't the case
   switch (archreg) {
   case  0: return offsetof(VexGuestARMState,guest_R0);
   case  1: return offsetof(VexGuestARMState,guest_R1);
   case  2: return offsetof(VexGuestARMState,guest_R2);
   case  3: return offsetof(VexGuestARMState,guest_R3);
   case  4: return offsetof(VexGuestARMState,guest_R4);
   case  5: return offsetof(VexGuestARMState,guest_R5);
   case  6: return offsetof(VexGuestARMState,guest_R6);
   case  7: return offsetof(VexGuestARMState,guest_R7);
   case  8: return offsetof(VexGuestARMState,guest_R8);
   case  9: return offsetof(VexGuestARMState,guest_R9);
   case 10: return offsetof(VexGuestARMState,guest_R10);
   case 11: return offsetof(VexGuestARMState,guest_R11);
   case 12: return offsetof(VexGuestARMState,guest_R12);
   case 13: return offsetof(VexGuestARMState,guest_R13);
   case 14: return offsetof(VexGuestARMState,guest_R14);
   case 15: return offsetof(VexGuestARMState,guest_R15);
   }

   vpanic("integerGuestRegOffset(arm,le)"); /*notreached*/
}

static IRExpr* getIReg ( UInt archreg )
{
   vassert(archreg < 16);
   return IRExpr_Get( integerGuestRegOffset(archreg), Ity_I32 );
}

/* Ditto, but write to a reg instead. */
static void putIReg ( UInt archreg, IRExpr* e )
{
   vassert(archreg < 16);
   stmt( IRStmt_Put(integerGuestRegOffset(archreg), e) );
}

static void assign ( IRTemp dst, IRExpr* e )
{
   stmt( IRStmt_Tmp(dst, e) );
}

static void storeLE ( IRExpr* addr, IRExpr* data )
{
   stmt( IRStmt_Store(Iend_LE,addr,data) );
}

static IRExpr* unop ( IROp op, IRExpr* a )
{
   return IRExpr_Unop(op, a);
}

static IRExpr* binop ( IROp op, IRExpr* a1, IRExpr* a2 )
{
   return IRExpr_Binop(op, a1, a2);
}

static IRExpr* mkexpr ( IRTemp tmp )
{
   return IRExpr_Tmp(tmp);
}

static IRExpr* mkU8 ( UChar i )
{
   return IRExpr_Const(IRConst_U8(i));
}

#if 0
static IRExpr* mkU16 ( UInt i )
{
   vassert(i < 65536);
   return IRExpr_Const(IRConst_U16(i));
}
#endif

static IRExpr* mkU32 ( UInt i )
{
   return IRExpr_Const(IRConst_U32(i));
}

#if 0
static IRExpr* mkU ( IRType ty, UInt i )
{
   if (ty == Ity_I8)  return mkU8(i);
   if (ty == Ity_I16) return mkU16(i);
   if (ty == Ity_I32) return mkU32(i);
   /* If this panics, it usually means you passed a size (1,2,4)
      value as the IRType, rather than a real IRType. */
   vpanic("mkU(ARM)");
}
#endif

static IRExpr* loadLE ( IRType ty, IRExpr* data )
{
   return IRExpr_Load(Iend_LE,ty,data);
}

#if 0
static IROp mkSizedOp ( IRType ty, IROp op8 )
{
   Int adj;
   vassert(ty == Ity_I8 || ty == Ity_I16 || ty == Ity_I32);
   vassert(op8 == Iop_Add8 || op8 == Iop_Sub8 
           || op8 == Iop_Mul8 
           || op8 == Iop_Or8 || op8 == Iop_And8 || op8 == Iop_Xor8
           || op8 == Iop_Shl8 || op8 == Iop_Shr8 || op8 == Iop_Sar8
           || op8 == Iop_CmpEQ8 || op8 == Iop_CmpNE8
           || op8 == Iop_Not8 );
   adj = ty==Ity_I8 ? 0 : (ty==Ity_I16 ? 1 : 2);
   return adj + op8;
}
#endif

#if 0
static IROp mkWidenOp ( Int szSmall, Int szBig, Bool signd )
{
   if (szSmall == 1 && szBig == 4) {
      return signd ? Iop_8Sto32 : Iop_8Uto32;
   }
   if (szSmall == 1 && szBig == 2) {
      return signd ? Iop_8Sto16 : Iop_8Uto16;
   }
   if (szSmall == 2 && szBig == 4) {
      return signd ? Iop_16Sto32 : Iop_16Uto32;
   }
   vpanic("mkWidenOp(ARM,guest)");
}
#endif














/*------------------------------------------------------------*/
/*--- Helpers for %flags.                                 ---*/
/*------------------------------------------------------------*/

/* -------------- Evaluating the flags-thunk. -------------- */

#if 0
/* Build IR to calculate all the flags from stored
   CC_OP/CC_DEP1/CC_DEP2/CC_NDEP.
   Returns an expression :: Ity_I32. */
static IRExpr* mk_armg_calculate_flags_all ( void )
{
   IRExpr** args
      = mkIRExprVec_3( IRExpr_Get(OFFB_CC_OP,   Ity_I32),
                       IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
                       IRExpr_Get(OFFB_CC_DEP2, Ity_I32) );
   IRExpr* call
      = mkIRExprCCall(
           Ity_I32,
           0/*regparm*/, 
           "armg_calculate_flags_all", &armg_calculate_flags_all,
           args
        );

   /* Exclude OP from definedness checking.  We're only
      interested in DEP1 and DEP2. */
   call->Iex.CCall.cee->mcx_mask = 1;
   return call;
}
#endif

/* Build IR to calculate just the carry flag from stored
   CC_OP/CC_DEP1/CC_DEP2/CC_NDEP.  Returns an expression :: Ity_I32. */
static IRExpr* mk_armg_calculate_flags_c ( void )
{
   IRExpr** args
      = mkIRExprVec_3( IRExpr_Get(OFFB_CC_OP,   Ity_I32),
                       IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
                       IRExpr_Get(OFFB_CC_DEP2, Ity_I32) );
   IRExpr* call
      = mkIRExprCCall(
           Ity_I32,
           0/*regparm*/, 
           "armg_calculate_flags_c", &armg_calculate_flags_c,
           args
        );
   /* Exclude OP from definedness checking.  We're only
      interested in DEP1 and DEP2. */
   call->Iex.CCall.cee->mcx_mask = 1;
   return call;
}


/* Build IR to calculate some particular condition from stored
   CC_OP/CC_DEP1/CC_DEP2.  Returns an expression
   of type Ity_I1.
*/
static IRExpr* mk_armg_calculate_condition ( ARMCondcode cond )
{
   IRExpr** args
      = mkIRExprVec_4( mkU32(cond),
                       IRExpr_Get(OFFB_CC_OP,  Ity_I32),
                       IRExpr_Get(OFFB_CC_DEP1, Ity_I32),
                       IRExpr_Get(OFFB_CC_DEP2, Ity_I32) );
   IRExpr* call
      = mkIRExprCCall(
           Ity_I32,
           0/*regparm*/, 
           "armg_calculate_condition", &armg_calculate_condition,
           args
        );

   /* Exclude the requested condition and OP from definedness
      checking.  We're only interested in DEP1 and DEP2. */
   call->Iex.CCall.cee->mcx_mask = (1<<0) | (1<<1);
   return unop(Iop_32to1, call);
}







/* -------------- Building the flags-thunk. -------------- */

/* The machinery in this section builds the flag-thunk following a
   flag-setting operation.  Hence the various setFlags_* functions.
*/

#if 0
static Bool isAddSub ( IROp op8 )
{
   return op8 == Iop_Add8 || op8 == Iop_Sub8;
}
#endif

#if 0
static Bool isLogic ( IROp op8 )
{
   return op8 == Iop_And8 || op8 == Iop_Or8 || op8 == Iop_Xor8;
}
#endif

/* U-widen 8/16/32 bit int expr to 32. */
static IRExpr* widenUto32 ( IRExpr* e )
{
   switch (typeOfIRExpr(irbb->tyenv,e)) {
   case Ity_I32: return e;
   case Ity_I16: return unop(Iop_16Uto32,e);
   case Ity_I8:  return unop(Iop_8Uto32,e);
   default: vpanic("widenUto32");
   }
}

#if 0
/* S-widen 8/16/32 bit int expr to 32. */
static IRExpr* widenSto32 ( IRExpr* e )
{
   switch (typeOfIRExpr(irbb->tyenv,e)) {
   case Ity_I32: return e;
   case Ity_I16: return unop(Iop_16Sto32,e);
   case Ity_I8:  return unop(Iop_8Sto32,e);
   default: vpanic("widenSto32");
   }
}
#endif

/* Narrow 8/16/32 bit int expr to 8/16/32.  Clearly only some
   of these combinations make sense. */
static IRExpr* narrowTo ( IRType dst_ty, IRExpr* e )
{
   IRType src_ty = typeOfIRExpr(irbb->tyenv,e);
   if (src_ty == dst_ty)
      return e;
   if (src_ty == Ity_I32 && dst_ty == Ity_I16)
      return unop(Iop_32to16, e);
   if (src_ty == Ity_I32 && dst_ty == Ity_I8)
      return unop(Iop_32to8, e);

   vex_printf("\nsrc, dst tys are: ");
   ppIRType(src_ty);
   vex_printf(", ");
   ppIRType(dst_ty);
   vex_printf("\n");
   vpanic("narrowTo(ARM)");
}


/* Set the flags thunk OP, DEP1 and DEP2 fields.  The supplied op is
   auto-sized up to the real op. */

static 
void setFlags_DEP1_DEP2 ( IROp op, IRTemp dep1, IRTemp dep2 )
{
   stmt( IRStmt_Put( OFFB_CC_OP,   mkU32(op)) );
   stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(dep1))) );
   stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(mkexpr(dep2))) );
}


/* Set the OP and DEP1 fields only, and write zero to DEP2. */

#if 0
static 
void setFlags_DEP1 ( IROp op, IRTemp dep1 )
{
   stmt( IRStmt_Put( OFFB_CC_OP,   mkU32(op)) );
   stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(dep1))) );
   stmt( IRStmt_Put( OFFB_CC_DEP2, mkU32(0)) );
}
#endif

#if 0
/* For shift operations, we put in the result and the undershifted
   result.  Except if the shift amount is zero, the thunk is left
   unchanged. */

static void setFlags_DEP1_DEP2_shift ( IROp    op,
                                       IRTemp  res,
                                       IRTemp  resUS,
                                       IRTemp  guard )
{
   vassert(guard);
   
   /* DEP1 contains the result, DEP2 contains the undershifted value. */
   stmt( IRStmt_Put( OFFB_CC_OP,
                     IRExpr_Mux0X( mkexpr(guard),
                                   IRExpr_Get(OFFB_CC_OP,Ity_I32),
                                   mkU32(op))) );
   stmt( IRStmt_Put( OFFB_CC_DEP1,
                     IRExpr_Mux0X( mkexpr(guard),
                                   IRExpr_Get(OFFB_CC_DEP1,Ity_I32),
                                   widenUto32(mkexpr(res)))) );
   stmt( IRStmt_Put( OFFB_CC_DEP2, 
                     IRExpr_Mux0X( mkexpr(guard),
                                   IRExpr_Get(OFFB_CC_DEP2,Ity_I32),
                                   widenUto32(mkexpr(resUS)))) );
}
#endif





#if 0
/* Multiplies are pretty much like add and sub: DEP1 and DEP2 hold the
   two arguments. */

static
void setFlags_MUL ( IRTemp arg1, IRTemp arg2, UInt op )
{
   stmt( IRStmt_Put( OFFB_CC_OP, mkU32(op) ) );
   stmt( IRStmt_Put( OFFB_CC_DEP1, widenUto32(mkexpr(arg1)) ));
   stmt( IRStmt_Put( OFFB_CC_DEP2, widenUto32(mkexpr(arg2)) ));
}
#endif









/* -------------- Condition codes. -------------- */

/* Condition codes, using the ARM encoding.  */

static HChar* name_ARMCondcode ( ARMCondcode cond )
{
   switch (cond) {
   case ARMCondEQ:  return "{eq}";
   case ARMCondNE:  return "{ne}";
   case ARMCondHS:  return "{hs}";  // or 'cs'
   case ARMCondLO:  return "{lo}";  // or 'cc'
   case ARMCondMI:  return "{mi}";
   case ARMCondPL:  return "{pl}";
   case ARMCondVS:  return "{vs}";
   case ARMCondVC:  return "{vc}";
   case ARMCondHI:  return "{hi}";
   case ARMCondLS:  return "{ls}";
   case ARMCondGE:  return "{ge}";
   case ARMCondLT:  return "{lt}";
   case ARMCondGT:  return "{gt}";
   case ARMCondLE:  return "{le}";
   case ARMCondAL:  return "";      // {al}: default, doesn't need specifying
   case ARMCondNV:  return "{nv}";
   default: vpanic("name_ARMCondcode");
   }
}

#if 0
static 
ARMCondcode positiveIse_ARMCondcode ( ARMCondcode  cond,
                                      Bool*     needInvert )
{
   vassert(cond >= ARMCondEQ && cond <= ARMCondNV);
   if (cond & 1) {
      *needInvert = True;
      return cond-1;
   } else {
      *needInvert = False;
      return cond;
   }
}
#endif


/* Addressing Mode 1 - DP ops
   Addressing Mode 2 - Load/Store word/ubyte (scaled)
 */
static HChar* name_ARMShiftOp ( UChar shift_op, UChar imm_val )
{
   switch (shift_op) {
   case 0x0: case 0x1: case 0x8: return "lsl";
   case 0x2: case 0x3: case 0xA: return "lsr";
   case 0x4: case 0x5: case 0xC: return "asr";
   case 0x6:                     return (imm_val==0) ? "rrx" : "ror";
   case 0x7: case 0xE:           return "ror";
   default: vpanic("name_ARMShiftcode");
   }
}


/* Addressing Mode 4 - Load/Store Multiple */
static HChar* name_ARMAddrMode4 ( UChar mode )
{
   /* See ARM ARM A5-55 for alternative names for stack operations
      ldmfa (full ascending), etc. */
   switch (mode) {
   case 0x0: return "da"; // Decrement after
   case 0x1: return "ia"; // Increment after
   case 0x2: return "db"; // Decrement before
   case 0x3: return "ib"; // Increment before
   default: vpanic("name_ARMAddrMode4");
   }
}

/* Data Processing ops */
static HChar* name_ARMDataProcOp ( UChar opc )
{
   switch (opc) {
   case 0x0: return "and";
   case 0x1: return "eor";
   case 0x2: return "sub";
   case 0x3: return "rsb";
   case 0x4: return "add";
   case 0x5: return "adc";
   case 0x6: return "sbc";
   case 0x7: return "rsc";
   case 0x8: return "tst";
   case 0x9: return "teq";
   case 0xA: return "cmp";
   case 0xB: return "cmn";
   case 0xC: return "orr";
   case 0xD: return "mov";
   case 0xE: return "bic";
   case 0xF: return "mvn";
   default: vpanic("name_ARMDataProcOp");
   }
}



/*
  Addressing mode 4 - LOAD/STORE multiple, LDM|STM
  ARM ARM A5-48
*/
static
Bool dis_loadstore_mult ( UInt theInstr )
{
   UChar flags   = toUChar((theInstr >> 20) & 0x1F); // theInstr[24:20]
   UChar Rn_addr = toUChar((theInstr >> 16) & 0xF);
   IRTemp Rn = newTemp(Ity_I32);
   IRTemp Rn_orig = newTemp(Ity_I32);
   UInt reg_list = theInstr & 0xFFFF;  // each bit addresses a register: R15 to R0
   
              // Load(1) | Store(0)
   UChar L  = toUChar((flags >> 0) & 1);
              // (W)riteback Rn (incr(U=1) | decr(U=0) by n_bytes)
   UChar W  = toUChar((flags >> 1) & 1);
              // Priviledged mode flag - *** CAB TODO ***
   UChar S  = toUChar((flags >> 2) & 1);  
              // Txfr ctl: Direction = upwards(1) | downwards(0)
   UChar U  = toUChar((flags >> 3) & 1);
              // Txfr ctl: Rn within(P=1) | outside(P=0) accessed mem
   UChar PU = toUChar((flags >> 3) & 3);  
   
   IRTemp start_addr = newTemp(Ity_I32);
   IRTemp end_addr   = newTemp(Ity_I32);
   IRTemp data=0;
   UInt n_bytes=0;
   UInt tmp_reg = reg_list;
   UInt reg_idx, offset;
   Bool decode_ok = True;
   
   HChar* cond_name = name_ARMCondcode( (theInstr >> 28) & 0xF );
   HChar reg_names[70];
   UInt buf_offset;
   
   while (tmp_reg > 0) {     // Count num bits in reg_list => num_bytes
      if (tmp_reg & 1) { n_bytes += 4; }
      tmp_reg = tmp_reg >> 1;
   }
   
   assign( Rn, getIReg(Rn_addr) );
   assign( Rn_orig, mkexpr(Rn) );
   
   switch (PU) {  // <addressing_mode>
   case 0x0:  // Decrement after  (DA)
      assign( start_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes + 4) ) );
      assign( end_addr,   mkexpr(Rn) );
      break;
      
   case 0x1:  // Increment after  (IA)
      assign( start_addr, mkexpr(Rn) );
      assign( end_addr,   binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes - 4) ) );
      break;
      
   case 0x2:  // Decrement before (DB)
      assign( start_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(n_bytes) ) );
      assign( end_addr,   binop( Iop_Sub32, mkexpr(Rn), mkU32(4) ) );
      break;
      
   case 0x3:  // Increment before (IB)
      assign( start_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(4) ) );
      assign( end_addr,   binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes) ) );
      break;
      
   default:
      vex_printf("dis_loadstore_mult(ARM): No such case: 0x%x", PU);
      return False; 
   }

   if (W==1) {
      if (U==1) { // upwards
         putIReg( Rn_addr, binop( Iop_Add32, mkexpr(Rn), mkU32(n_bytes) ) );
      } else { // downwards
         putIReg( Rn_addr, binop( Iop_Sub32, mkexpr(Rn), mkU32(n_bytes) ) );
      }
   }
   
   
   /*
     Loop through register list, LOAD/STORE indicated registers
     Lowest numbered reg -> lowest address, so start with lowest register
     reg_idx: guest register address
     offset : current mem offset from start_addr
   */
   reg_names[0] = '\0';
   buf_offset=0;
   offset=0;
   for (reg_idx=0; reg_idx < 16; reg_idx++) {
      if (( reg_list >> reg_idx ) & 1) {  // reg_list[i] == 1?
         
         if (L==1) { // LOAD Ri, (start_addr + offset)
            
            if (Rn_addr == reg_idx && W==1) { // Undefined - ARM ARM A4-31
               decode_ok=False;
               break;
            }
            
            assign( data, loadLE(Ity_I32, binop(Iop_Add32,
                                                mkexpr(start_addr),
                                                mkU32(offset))) );
            if (reg_idx == 15) {
               // assuming architecture < 5: See ARM ARM A4-31
               putIReg( reg_idx, binop(Iop_And32, mkexpr(data), mkU32(0xFFFFFFFC)) );
            } else {
               putIReg( reg_idx, mkexpr(data) );
            }
         } else {    // STORE Ri, (start_addr + offset)
            
            // ARM ARM A4-85 (Operand restrictions)
            if (reg_idx == Rn_addr && W==1) {  // Rn in reg_list && writeback
               if (offset != 0) {  // Undefined - See ARM ARM A4-85
                  decode_ok=False;
                  break;
               }
               // is lowest reg in reg_list: store Rn_orig
               storeLE( mkexpr(start_addr), mkexpr(Rn_orig) );
            } else {
               storeLE( binop(Iop_Add32, mkexpr(start_addr), mkU32(offset) ),
                        getIReg(reg_idx) );
            }
         }
         offset += 4;
         
         reg_names[buf_offset++] = 'R';
         if (reg_idx > 9) {
            reg_names[buf_offset++] = '1';
            reg_names[buf_offset++] = (HChar)toUChar(38 + reg_idx);
         } else {
            reg_names[buf_offset++] = (HChar)toUChar(48 + reg_idx);
         }
         reg_names[buf_offset++] = ',';
         // CAB: Eugh!  Where's strcpy?!
      }
   }
   if (buf_offset > 0) {
      reg_names[buf_offset-1] = '\0';
   }
   DIP("%s%s%s R%d%s, {%s}%s\n", (L==1) ? "ldm":"stm", cond_name,
       name_ARMAddrMode4( PU ), Rn_addr, (W==1) ? "!" : "",
       reg_names, (S==1) ? "^" : "");
   
   // CAB TODO:
   // IR assert( end_addr == (start_addr + offset) - 8 )
   
   if (offset == 0) {   // Unpredictable - ARM ARM A5-21
      vex_printf("dis_loadstore_mult(arm): Unpredictable - offset==0\n");
      decode_ok = False;
   }
   
   return decode_ok;
}





static
Bool dis_loadstore_w_ub_address ( UInt theInstr, IRTemp* address, HChar* buf )
{
   UChar is_reg    = toUChar((theInstr >> 25) & 0x1); 
                     // immediate | register offset/index
   UInt  flags     =         (theInstr >> 20) & 0x3F; // theInstr[25:20]
   UChar Rn_addr   = toUChar((theInstr >> 16) & 0xF);
   UChar Rm_addr   = toUChar((theInstr >> 00) & 0xF);
   UChar shift_op  = toUChar((theInstr >> 04) & 0xFF);
   UInt  offset_12 =         (theInstr >> 00) & 0xFFF;
   IRTemp Rn = newTemp(Ity_I32);
   IRTemp Rm = newTemp(Ity_I32);
   UChar shift_imm, shift;
   
   UChar W = toUChar((flags >> 1) & 1); // base register writeback flag - See *Note
   UChar U = toUChar((flags >> 3) & 1); // offset is added(1)|subtracted(0) from the base
   UChar P = toUChar((flags >> 4) & 1); // addressing mode flag - See *Note
   /* *Note
      P==0: post-indexed addressing: addr -> Rn
      W==0: normal mem access
      W==1: unprivileged mem access
      P==1: W==0: offset addressing: Rn not updated  - ARM ARM A5-20
      W==1: pre-indexed addressing: addr -> Rn
   */
   
   IRTemp scaled_index = newTemp(Ity_I32);
   IRTemp reg_offset = newTemp(Ity_I32);
   
   IRTemp oldFlagC = newTemp(Ity_I32);
   
   HChar buf2[30];
   HChar buf3[20];
   buf3[0] = '\0';
   
   if (Rn_addr == 15) {
      if (P==1 && W==0) { // offset addressing
         // CAB: This right?
         assign( Rn, binop(Iop_And32, getIReg(15), mkU32(8)) );
      } else {                        // Unpredictable - ARM ARM A5-25,29...
         vex_printf("dis_loadstore_w_ub_address(arm): Unpredictable - Rn_addr==15\n");
         return False;
      }
   } else {
      assign( Rn, getIReg(Rn_addr) );
   }
   
   /*
     Retrieve / Calculate reg_offset
   */
   if (is_reg) {
      if (Rm_addr == 15) {               // Unpredictable - ARM ARM A5-21
         vex_printf("dis_loadstore_w_ub_address(arm): Unpredictable - Rm_addr==15\n");
         return False;
      }
      if (P==0 || W==1) {  // pre|post-indexed addressing
         if (Rm_addr == Rn_addr) {      // Unpredictable - ARM ARM A5-25
            vex_printf("dis_loadstore_w_ub_address(arm): Unpredictable - Rm_addr==Rn_addr\n");
            return False;
         }
      }
      assign( Rm, getIReg(Rm_addr) );
      
      if (shift_op == 0) {  // Register addressing
         assign( reg_offset, mkexpr(Rm) );
      } else {              // Scaled Register addressing
         shift_imm = toUChar((shift_op >> 3) & 0x1F);
         shift     = toUChar((shift_op >> 1) & 0x3);
         
         switch (shift) {
         case 0x0: // LSL
            assign( scaled_index, binop(Iop_Shl32, mkexpr(Rm), mkU8(shift_imm)) );
            break;
            
         case 0x1: // LSR
            if (shift_imm) {
               assign( scaled_index, binop(Iop_Shr32, mkexpr(Rm), mkU8(shift_imm)) );
            } else {
               assign( scaled_index, mkU32(0) );
            }
            break;
            
         case 0x2: // ASR
            if (shift_imm) {
               assign( scaled_index, binop(Iop_Sar32, mkexpr(Rm), mkU32(shift_imm)) );
            } else {
               assign( scaled_index,     // Rm[31] ? 0xFFFFFFFF : 0x0
                       IRExpr_Mux0X(binop(Iop_And32, mkexpr(Rm), mkU32(0x8FFFFFFF)),
                                    mkexpr(0x0), mkexpr(0xFFFFFFFF)) );
            }
            break;
            
         case 0x3: // ROR|RRX
            assign( oldFlagC, binop(Iop_Shr32,
                                    mk_armg_calculate_flags_c(),
                                    mkU8(ARMG_CC_SHIFT_C)) );
            
            if (shift_imm == 0) { // RRX (ARM ARM A5-17)
               // 33 bit ROR using carry flag as the 33rd bit
               // op = Rm >> 1, carry flag replacing vacated bit position.  
               // scaled_index = (c_flag << 31) | (Rm >> 1)
               assign( scaled_index, binop(Iop_Or32,
                                           binop(Iop_Shl32, mkexpr(oldFlagC), mkU32(31)),
                                           binop(Iop_Shr32, mkexpr(Rm),  mkU8(1))) );
               
            } else { // ROR
               // scaled_index = Rm ROR shift_imm
               //              = (Rm >> shift_imm) | (Rm << (32-shift_imm))
               assign( scaled_index,
                       binop(Iop_Or32,
                             binop(Iop_Shr32, mkexpr(Rm), mkU8(shift_imm)),
                             binop(Iop_Shl32, mkexpr(Rm),
                                   binop(Iop_Sub8, mkU8(32), mkU32(shift_imm)))) );
            }
            break;
            
         default:
            vex_printf("dis_loadstore_w_ub(ARM): No such case: 0x%x", shift);
            return False; 
         }
         assign( reg_offset, mkexpr(scaled_index) );
         
         if (shift == 0x3 && shift_imm == 0) {
            DIS(buf3, ", %s", name_ARMShiftOp(toUChar(shift_op * 2), shift_imm));
         } else {
            DIS(buf3, ", %s #%d", 
                      name_ARMShiftOp(toUChar(shift_op * 2), shift_imm), 
                      shift_imm);
         }
      }
      DIS(buf2, "%cR%d%s", (U==1) ? '+' : '-', Rm_addr, buf3);
   } else { // immediate
      assign( reg_offset, mkU32(offset_12) );
      
      DIS(buf2, "#%c%u", (U==1) ? '+' : '-', offset_12);
   }
   DIS(buf, "[R%d%s, %s%s", Rn_addr,
       (P==0) ? "]" : "", buf2,
       (P==1) ? ((W==1) ? "]!" : "]") : "");
   
   /*
     Depending on P,U,W, write to Rn and set address to load/store
   */
   if (P==1) {       // offset | pre-indexed addressing
      if (U == 1) { // - increment
         assign( *address, binop(Iop_Add32, mkexpr(Rn), mkexpr(reg_offset)) );
      } else {      // - decrement
         assign( *address, binop(Iop_Sub32, mkexpr(Rn), mkexpr(reg_offset)) );
      }
      if (W == 1) { // pre-indexed addressing, base register writeback
         putIReg( Rn_addr, mkexpr(*address) );
      }
   } else {          // post-indexed addressing
      assign( *address, mkexpr(Rn) );
      if (U == 1) { // - increment
         putIReg( Rn_addr, binop( Iop_Add32, mkexpr(Rn), mkexpr(reg_offset) ) );
      } else {      // - decrement
         putIReg( Rn_addr, binop( Iop_Sub32, mkexpr(Rn), mkexpr(reg_offset) ) );
      }
   }
   return True;
}




/*
  Addressing mode 2 - LOAD/STORE word or unsigned byte
  ARM ARM A5-18
*/
static
Bool dis_loadstore_w_ub ( UInt theInstr )
{
   UInt   flags   =         (theInstr >> 20) & 0x3F;  // theInstr[25:20]
   UChar  Rn_addr = toUChar((theInstr >> 16) & 0xF);
   UChar  Rd_addr = toUChar((theInstr >> 12) & 0xF);
   IRTemp address = newTemp(Ity_I32);
  
   UChar L  = toUChar((flags >> 0) & 1); // Load(1) | Store(0)
   UChar W  = toUChar((flags >> 1) & 1); // base register writeback
   UChar B  = toUChar((flags >> 2) & 1); // access = unsigned byte(1) | word(0)
   
   IRTemp value      = newTemp(Ity_I32);
   IRTemp data       = newTemp(Ity_I32);
   IRTemp data_ror8  = newTemp(Ity_I32);
   IRTemp data_ror16 = newTemp(Ity_I32);
   IRTemp data_ror24 = newTemp(Ity_I32);
   IRExpr* expr_addr_10;
   HChar* cond_name = name_ARMCondcode( (theInstr >> 28) & 0xF );
   HChar dis_buf[50];
   
   
   vassert(((theInstr >> 26) & 0x3) == 0x1);
   
   // Get the address to load/store
   if (!dis_loadstore_w_ub_address(theInstr, &address, dis_buf)) { return False; }
   
   DIP("%s%s%s R%d, %s\n", (L==1) ? "ldr" : "str", cond_name,
       (B==1) ? "b" : "", Rd_addr, dis_buf);
   
   if (Rd_addr == Rn_addr && W==1) {  // Unpredictable - ARM ARM A4-39,41,89,91
      vex_printf("dis_loadstore_w_ub(arm): Unpredictable - Rd_addr==Rn_addr\n");
      return False;
   }
   
   /*
     LOAD/STORE Rd, address
   */
   if (L==1) { // LOAD
      if (B==1) {  // unsigned byte (LDRB): ARM ARM A4-40
         if (Rd_addr == 15) {  // Unpredictable - ARM ARM A4-40
            vex_printf("dis_loadstore_w_ub(arm): Unpredictable - Rd_addr==15\n");
            return False;
         }
         putIReg( Rd_addr, loadLE( Ity_I8, mkexpr( address ) ) );
      }
      else {       // word (LDR): ARM ARM A4-38
         expr_addr_10 = binop(Iop_And32, mkexpr(address), mkU32(0x3));
         
         /*
           CAB TODO
           if (Rd_addr == 15 && address[1:0] == 0) => Unpredictable
           How to bomb out using IR?
         */
         
         /* LOAD memory data (4 bytes) */
         assign( data, loadLE( Ity_I32, mkexpr( address ) ) );
         
         // data ROR 8
         assign( data_ror8, binop(Iop_Sub8, mkU8(32), mkU32(8)) ); 
         assign( data_ror8,
                 binop( Iop_Or32,
                        binop( Iop_Shr32, mkexpr(data), mkU8(8) ),
                        binop( Iop_Shl32, mkexpr(data), mkexpr(data_ror8) )));
         // data ROR 16
         assign( data_ror16, binop(Iop_Sub8, mkU8(32), mkU32(16)) );
         assign( data_ror16,
                 binop( Iop_Or32,
                        binop( Iop_Shr32, mkexpr(data), mkU8(16) ),
                        binop( Iop_Shl32, mkexpr(data), mkexpr(data_ror16) )));
         
         // data ROR 24
         assign( data_ror24, binop(Iop_Sub8, mkU8(32), mkU32(24)) );
         assign( data_ror24,
                 binop( Iop_Or32,
                        binop( Iop_Shr32, mkexpr(data), mkU8(24) ),
                        binop( Iop_Shl32, mkexpr(data), mkexpr(data_ror24) )));
         
         /* switch (address[1:0]) {
            0x0: value = data;
            0x1: value = data ROR 8;
            0x2: value = data ROR 16;
            0x3: value = data ROR 24; } */
         assign( value, IRExpr_Mux0X(
                    binop(Iop_CmpEQ32, expr_addr_10, mkU32(0x0)),
                    IRExpr_Mux0X(
                       binop(Iop_CmpEQ32, expr_addr_10, mkU32(0x1)),
                       IRExpr_Mux0X(
                          binop(Iop_CmpEQ32, expr_addr_10, mkU32(0x2)),
                          mkexpr(data_ror24),
                          mkexpr(data_ror16) ),
                       mkexpr(data_ror8) ),
                    mkexpr(data) ) );
         
         if (Rd_addr == 15) {
            // assuming architecture < 5: See ARM ARM A4-28
            putIReg( Rd_addr, binop(Iop_And32, mkexpr(value), mkU32(0xFFFFFFFC)) );
            
            // CAB: Need to tell vex we're doing a jump here?
            // irbb->jumpkind = Ijk_Boring;
            // irbb->next     = mkexpr(value);
         } else {
            putIReg( Rd_addr, mkexpr(value) );
         }
         
      }
   } else { // STORE: ARM ARM A4-88
      if (B==1) {  // unsigned byte
         if (Rd_addr == 15) {  // Unpredictable - ARM ARM A4-90
            vex_printf("dis_loadstore_w_ub(arm): Unpredictable - Rd_addr==15\n");
            return False;
         }
         storeLE( mkexpr(address), unop(Iop_32to8, getIReg(Rd_addr)) );   // Rd[7:0]
      } else {     // word
         
         if (Rd_addr == 15) {  // Implementation Defined - ARM ARM A4-88
            vex_printf("dis_loadstore_w_ub(arm): Implementation Defined - Rd_addr==15\n");
            return False;
            // CAB TODO: What to do here?
         }
         storeLE( mkexpr(address), getIReg(Rd_addr) );
      }
   }
   return True;
}






/*
  ARMG_CC_OP_LSL, ARMG_CC_OP_LSR, ARMG_CC_OP_ASR
  ARM ARM A5-9...

  carry = carry_out[0]
*/
static
IRExpr* dis_shift( Bool* decode_ok, UInt theInstr, IRTemp* carry_out, HChar* buf )
{
   UChar   Rn_addr     = toUChar((theInstr >> 16) & 0xF);
   UChar   Rd_addr     = toUChar((theInstr >> 12) & 0xF);
   UChar   Rs_addr     = toUChar((theInstr >>  8) & 0xF);
   UChar   Rm_addr     = toUChar((theInstr >>  0) & 0xF);
   UChar   by_reg      = toUChar((theInstr >>  4) & 0x1);  // instr[4]
   UChar   shift_imm   = toUChar((theInstr >>  7) & 0x1F); // instr[11:7]
   UChar   shift_op    = toUChar((theInstr >>  4) & 0xF);  // instr[7:4]
   IRTemp  Rm          = newTemp(Ity_I32);
   IRTemp  Rs          = newTemp(Ity_I32);
   IRTemp  shift_amt   = newTemp(Ity_I8);
   IRTemp  carry_shift = newTemp(Ity_I8);
   IRTemp  oldFlagC    = newTemp(Ity_I32);
   IRTemp  mux_false   = newTemp(Ity_I32);
   IRExpr* expr;
   IROp    op;
   
   assign( oldFlagC, binop(Iop_Shr32,
                           mk_armg_calculate_flags_c(),
                           mkU8(ARMG_CC_SHIFT_C)) );
   
   switch (shift_op) {
   case 0x0: case 0x8: case 0x1: op = Iop_Shl32; break;
   case 0x2: case 0xA: case 0x3: op = Iop_Shr32; break;
   case 0x4: case 0xC: case 0x5: op = Iop_Sar32; break;
   default:
      vex_printf("dis_shift(arm): No such case: 0x%x\n", shift_op);
      *decode_ok = False;
      return mkU32(0);
   }
   
   
   if (by_reg) {  // Register Shift
      assign( Rm, getIReg(Rm_addr) );
      
      if (Rd_addr == 15 || Rm_addr == 15 ||
          Rn_addr == 15 || Rs_addr == 15) {   // Unpredictable (ARM ARM A5-10)
         vex_printf("dis_shift(arm): Unpredictable - Rd|Rm|Rn|Rs == R15\n");
         *decode_ok = False;
         return mkU32(0);
      }
      
      assign( Rs, getIReg((theInstr >> 8) & 0xF) );  // instr[11:8]
      
      // shift_amt = shift_expr & 31   => Rs[5:0]
      assign( shift_amt,
              narrowTo(Ity_I8, binop( Iop_And32, mkexpr(Rs), mkU32(0x1F)) ) );
      
      // CAB TODO: support for >31 shift ?  (Rs[7:0])
      
      switch (shift_op) {
      case 0x1: // LSL(reg)
         assign( mux_false, mkU32(0) );
         assign( carry_shift, binop(Iop_Add8, mkU8(32), mkexpr(shift_amt)) );
         break;
         
      case 0x3: // LSR(reg)
         assign( mux_false, mkU32(0) );
         assign( carry_shift, binop(Iop_Sub8, mkexpr(shift_amt), mkU8(1)) );
         break;
         
      case 0x5: // ASR(reg)
         // Rs[31] == 0 ? 0x0 : 0xFFFFFFFF
         assign( mux_false,
                 IRExpr_Mux0X(
                    binop(Iop_CmpLT32U, mkexpr(Rs), mkU32(0x80000000)),
                    mkU32(0xFFFFFFFF), mkU32(0) ) );
         assign( carry_shift,
                 binop(Iop_Sub8, mkexpr(shift_amt), mkU8(1)) );
         break;
         
      default:
         vex_printf("dis_shift(arm): Reg shift: No such case: 0x%x\n", shift_op);
         *decode_ok = False;
         return mkU32(0);
      }
      
      expr = IRExpr_Mux0X( 
         binop(Iop_CmpLT32U, widenUto32(mkexpr(shift_amt)), mkU32(32)),
         mkexpr(mux_false),
         binop(op, mkexpr(Rm), mkexpr(shift_amt)) );
      
      // shift_amt == 0 ? old_flag_c : Rm >> x
      assign( *carry_out,
              IRExpr_Mux0X(
                 binop(Iop_CmpEQ8, mkexpr(shift_amt), mkU8(0)),
                 binop(Iop_Shr32, mkexpr(Rm), mkexpr(carry_shift)),
                 mkexpr(oldFlagC) ) );
      
      DIS(buf, "R%d, %s R%d", Rm_addr, name_ARMShiftOp(shift_op, 0), Rs_addr);
   }
   else {  // Immediate shift
      
      // CAB: This right?
      // "the value used is the address of the current intruction plus 8"
      if (Rm_addr == 15 || Rn_addr == 15) {        // ARM ARM A5-9
         assign( Rm, binop(Iop_Add32, getIReg(15), mkU32(8)) );
      } else {
         assign( Rm, getIReg(Rm_addr) );
      }
      
      if (shift_imm == 0) {
         switch (shift_op) {
         case 0x0: case 0x8: // LSL(imm)
            expr = mkexpr(Rm);
            assign( *carry_out, mkexpr(oldFlagC) );
            break;
            
         case 0x2: case 0xA: // LSR(imm)
            expr = mkexpr(0);
            // Rm >> 31: carry = R[0]
            assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm), mkU8(31)) );
            break;
            
         case 0x4: case 0xC: // ASR(imm)
            // Rs[31] == 0 ? 0x0 : 0xFFFFFFFF
            expr = IRExpr_Mux0X(
               binop(Iop_CmpLT32U, mkexpr(Rs), mkU32(0x80000000)),
               mkU32(0xFFFFFFFF), mkU32(0) );
            // Rm >> 31: carry = R[0]
            assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm), mkU8(31)) );
            break;
            
         default:
            vex_printf("dis_shift(arm): Imm shift: No such case: 0x%x\n", shift_op);
            *decode_ok = False;
            return mkU32(0);
         }
         DIS(buf, "R%d", Rm_addr);
      } else {
         expr = binop(op, mkexpr(Rm), mkU8(shift_imm));
         assign( *carry_out, binop(op, mkexpr(Rm),
                                   binop(Iop_Sub32, mkU32(shift_imm), mkU32(1)) ) );

         DIS(buf, "R%d, %s #%d", Rm_addr, name_ARMShiftOp(shift_op, 0), shift_imm);
      }
   }
   return expr;
}




/*
  ARMG_CC_OP_ROR
  ARM ARM A5-15,16,17
*/
static
IRExpr* dis_rotate ( Bool* decode_ok, UInt theInstr, IRTemp* carry_out, HChar* buf )
{
   UChar  Rn_addr  = toUChar((theInstr >> 16) & 0xF);
   UChar  Rd_addr  = toUChar((theInstr >> 12) & 0xF);
   UChar  Rs_addr  = toUChar((theInstr >> 8) & 0xF);
   UChar  Rm_addr  = toUChar((theInstr >> 0) & 0xF);
   UChar  by_reg   = toUChar((theInstr >> 4) & 0x1);  // instr[4]
   UChar  rot_imm  = toUChar((theInstr >> 7) & 0x1F); // instr[11:7]
   IRTemp Rm       = newTemp(Ity_I32);
   IRTemp Rs       = newTemp(Ity_I32);
   IRTemp rot_amt  = newTemp(Ity_I8);      // Rs[7:0]
   IRTemp oldFlagC = newTemp(Ity_I32);
   IRExpr* expr=0;
   
   assign( oldFlagC, binop(Iop_Shr32,
                           mk_armg_calculate_flags_c(),
                           mkU8(ARMG_CC_SHIFT_C)) );
   
   if (by_reg) {  // Register rotate
      assign( Rm, getIReg(Rm_addr) );
      
      if (Rd_addr == 15 || Rm_addr == 15 ||
          Rn_addr == 15 || Rs_addr == 15) {    // Unpredictable (ARM ARM A5-10)
         vex_printf("dis_rotate(arm): Unpredictable - Rd|Rm|Rn|Rs == R15\n");
         *decode_ok = False;
         return mkU32(0);
      }
      
      assign( Rs, getIReg((theInstr >> 8) & 0xF) );  // instr[11:8]
      // Rs[4:0]
      assign( rot_amt, narrowTo(Ity_I8,
                                binop(Iop_And32, mkexpr(Rs), mkU32(0x1F))) );
      
      // CAB: This right?
      // Rs[7:0] == 0 ? oldFlagC : (Rs[4:0] == 0 ? Rm >> 31 : Rm >> rot-1 )
      assign( *carry_out,
              IRExpr_Mux0X(
                 binop(Iop_CmpNE32, mkU32(0),
                       binop(Iop_And32, mkexpr(Rs), mkU32(0xFF))),
                 mkexpr(oldFlagC),
                 IRExpr_Mux0X(
                    binop(Iop_CmpEQ8, mkexpr(rot_amt), mkU8(0)),
                    binop(Iop_Shr32, mkexpr(Rm),
                          binop(Iop_Sub8, mkexpr(rot_amt), mkU8(1))),
                    binop(Iop_Shr32, mkexpr(Rm),
                          binop(Iop_Shr32, mkexpr(Rm), mkU8(31))) ) ) );
      
      
      /* expr = (dst0 >> rot_amt) | (dst0 << (wordsize-rot_amt)) */
      expr = binop(Iop_Or32,
                   binop(Iop_Shr32, mkexpr(Rm), mkexpr(rot_amt)),
                   binop(Iop_Shl32, mkexpr(Rm),
                         binop(Iop_Sub8, mkU8(32), mkexpr(rot_amt))));

      DIS(buf, "R%d, ror R%d", Rm_addr, Rs_addr);
   }
   else {  // Immediate rotate

      // CAB: This right?
      // "the value used is the address of the current intruction plus 8"
      if (Rm_addr == 15 || Rn_addr == 15) {        // ARM ARM A5-9
         assign( Rm, binop(Iop_Add32, getIReg(15), mkU32(8)) );
      } else {
         assign( Rm, getIReg(Rm_addr) );
      }
      
      // Rm >> rot-1: carry = R[0]
      assign( *carry_out, binop(Iop_Shr32, mkexpr(Rm),
                                binop(Iop_Sub8, mkU8(rot_imm), mkU8(1)) ) );
      
      if (rot_imm == 0) {   // RRX (ARM ARM A5-17)
         // 33 bit ROR using carry flag as the 33rd bit
         // op = Rm >> 1, carry flag replacing vacated bit position.  
         
         // CAB: This right?
         expr = binop(Iop_Or32,
                      binop(Iop_Shl32, mkexpr(oldFlagC), mkU8(31)),
                      binop(Iop_Shr32, mkexpr(Rm), mkU8(1)));
         DIS(buf, "R%d, rrx", Rm_addr);
      } else {
         expr = binop(Iop_Or32,
                      binop(Iop_Shr32, mkexpr(Rm), mkU8(rot_imm)),
                      binop(Iop_Shl32, mkexpr(Rm),
                            binop(Iop_Sub8, mkU8(32), mkU8(rot_imm))));
         
         DIS(buf, "R%d, ror #%u", Rm_addr, (UInt)rot_imm);
      }
   }
   return expr;
}




/*
  CAB TODO:
   - Not all shifts by 0 leave c_flag unchanged, so guard_expr is more difficult...
  assign( flags_guard, binop( Iop_CmpEQ32, mkexpr(shift_amt), mkU32(0) ) );
  setFlags_DEP1_DEP2_shift( ARMG_CC_OP_LSL, Rm, shift_op, flags_guard );
*/




/* Addressing mode 1 - Data Processing ops
   General syntax: <opcode>{<cond>}{S} <Rd>, <Rn>, <shifter_operand>
   Returns <shifter_operand> expression
*/
static
IRExpr* dis_shifter_op ( Bool *decode_ok, UInt theInstr, IRTemp* carry_out, HChar* buf )
{
   UChar is_immed  = toUChar((theInstr >> 25) & 1);  // immediate / register shift
   UChar shift_op  = toUChar((theInstr >> 4) & 0xF); // second byte
   UInt immed_8, rot_imm;
   UInt imm;
   IRTemp oldFlagC = newTemp(Ity_I32);
   
   if (is_immed) {  // ARM ARM A5-2
      // dst = src ROR rot << 1
      //     = (src >> rot) | (src << (32-rot));
      immed_8 = theInstr & 0xFF;
      rot_imm = ((theInstr >> 8) & 0xF) << 1;  
      imm = (immed_8 >> rot_imm) | (immed_8 << (32-rot_imm));
      
      if (rot_imm == 0) {
         assign( oldFlagC, binop(Iop_Shr32,
                                 mk_armg_calculate_flags_c(),
                                 mkU8(ARMG_CC_SHIFT_C)) );
         assign( *carry_out, mkexpr(oldFlagC) );
      } else {
         assign( *carry_out, binop(Iop_Shr32, mkU32(imm), mkU8(31)) );
      }
      DIS(buf, "#%u", imm);
      return mkU32(imm);
   } else {
      
      // We shouldn't have any 'op' with bits 4=1 and 7=1 : 1xx1
      switch (shift_op) {
      case 0x0: case 0x8: case 0x1:
      case 0x2: case 0xA: case 0x3: 
      case 0x4: case 0xC: case 0x5:
         return dis_shift( decode_ok, theInstr, carry_out, buf );
         
      case 0x6: case 0xE: case 0x7:
         return dis_rotate( decode_ok, theInstr, carry_out, buf );
         
      default: // Error: Any other value shouldn't be here.
         *decode_ok = False;
         vex_printf("dis_shifter_op(arm): shift: No such case: 0x%x\n", shift_op);
         return mkU32(0);
      }
   }
}





/* -------------- Helper for DPI's. --------------
*/
static
Bool dis_dataproc ( UInt theInstr )
{
   UChar opc       = toUChar((theInstr >> 21) & 0xF);
   UChar set_flags = toUChar((theInstr >> 20) & 1);
   UChar Rn_addr   = toUChar((theInstr >> 16) & 0xF);
   UChar Rd_addr   = toUChar((theInstr >> 12) & 0xF);
   IRTemp Rn = newTemp(Ity_I32);
   IRTemp Rd = newTemp(Ity_I32);
   IRTemp alu_out = newTemp(Ity_I32);
   IRTemp shifter_op = newTemp(Ity_I32);
   IRTemp carry_out = newTemp(Ity_I32);
   IROp op_set_flags = ARMG_CC_OP_LOGIC;
   Bool testing_instr = False;
   Bool decode_ok = True;
   HChar* cond_name = name_ARMCondcode( (theInstr >> 28) & 0xF );
   HChar* ch_set_flags = (set_flags == 1) ? "S" : "";
   HChar dis_buf[50];
   
   assign( shifter_op, dis_shifter_op( &decode_ok, theInstr, &carry_out, dis_buf ) );
   if (!decode_ok) return False;
   
   assign( Rd, getIReg(Rd_addr) );
   assign( Rn, getIReg(Rn_addr) );
   
   
   switch (opc) {
   case 0x0: case 0x1: case 0x2: case 0x3: case 0x4:
   case 0xC: case 0xE:
      DIP("%s%s%s R%d, R%d, %s\n", name_ARMDataProcOp(opc),
          cond_name, ch_set_flags, Rd_addr, Rn_addr, dis_buf);
      break;
   case 0x5: case 0x6: case 0x7:
      // CAB: Unimplemented
      break;
   case 0x8: case 0x9: case 0xA: case 0xB:
      DIP("%s%s R%d, %s\n", name_ARMDataProcOp(opc),
          cond_name, Rn_addr, dis_buf);
      break;
   case 0xD: case 0xF:
      DIP("%s%s%s R%d, %s\n", name_ARMDataProcOp(opc),
          cond_name, ch_set_flags, Rd_addr, dis_buf);
      break;
   default:break;
   }


   switch (opc) {
   case 0x0: // AND
      assign( alu_out, binop(Iop_And32, getIReg(Rn_addr), mkexpr(shifter_op)) );
      break;
       
   case 0x1: // EOR
      assign( alu_out, binop(Iop_Xor32, getIReg(Rn_addr), mkexpr(shifter_op)) );
      break;

   case 0x2: // SUB
      assign( alu_out, binop( Iop_Sub32, getIReg(Rn_addr), mkexpr(shifter_op) ) );
      op_set_flags = ARMG_CC_OP_SUB;
      break;

   case 0x3:  // RSB
      assign( alu_out, binop( Iop_Sub32, mkexpr(shifter_op), getIReg(Rn_addr) ) );
      op_set_flags = ARMG_CC_OP_SUB;
      /* set_flags(), below, switches the args for this case */
      break;

   case 0x4: // ADD
      assign( alu_out, binop( Iop_Add32, getIReg(Rn_addr), mkexpr(shifter_op) ) );
      op_set_flags = ARMG_CC_OP_ADD;
      break;

   case 0x5:  // ADC  // CAB: Unimplemented
   case 0x6:  // SBC  // CAB: Unimplemented
   case 0x7:  // RSC  // CAB: Unimplemented
      goto decode_failure;

   case 0x8: // TST
      vassert(set_flags==1);
      assign( alu_out, binop(Iop_And32, getIReg(Rn_addr), mkexpr(shifter_op)) );
      testing_instr = True;
      break;
      
   case 0x9: // TEQ
      vassert(set_flags==1);
      assign( alu_out, binop(Iop_Xor32, getIReg(Rn_addr), mkexpr(shifter_op)) );
      testing_instr = True;
      break;
      
   case 0xA: // CMP
      vassert(set_flags==1);
      op_set_flags = ARMG_CC_OP_SUB;
      testing_instr = True;
      break;

   case 0xB: // CMN
      vassert(set_flags==1);
      op_set_flags = ARMG_CC_OP_ADD;
      testing_instr = True;
      break;

   case 0xC: // ORR
      assign( alu_out, binop(Iop_Or32, getIReg(Rn_addr), mkexpr(shifter_op)) );
      break;

   case 0xD: // MOV
      assign( alu_out, mkexpr(shifter_op) );
      break;

   case 0xE: // BIC
      assign( alu_out, binop(Iop_And32, getIReg(Rn_addr),
                             unop( Iop_Not32, mkexpr(shifter_op))) );
      break;

   case 0xF: // MVN
      assign( alu_out, unop(Iop_Not32, mkexpr(shifter_op)) );
      break;

   default:
   decode_failure:
      vex_printf("dis_dataproc(arm): unhandled opcode: 0x%x\n", opc);
      return False;
   }

   if (!testing_instr) {
      if ( Rd_addr == 15) { // dest reg == PC
         // CPSR = SPSR: Unpredictable in User | System mode (no SPSR!)
         // Unpredictable - We're only supporting user mode...
         vex_printf("dis_dataproc(arm): Unpredictable - Rd_addr==15\n");
         return False;
      }
      putIReg( Rd_addr, mkexpr(alu_out) );
   }
   
   if (set_flags) {
      if (op_set_flags == ARMG_CC_OP_LOGIC) {
         setFlags_DEP1_DEP2( op_set_flags, alu_out, carry_out );
      } else {
         if (opc == 0x3) {
            setFlags_DEP1_DEP2( op_set_flags, shifter_op, Rn );
         } else {
            setFlags_DEP1_DEP2( op_set_flags, Rn, shifter_op );
         }
      }
   }
   return decode_ok;
}




/* -------------- Helper for Branch. --------------
*/
static
void dis_branch ( UInt theInstr )
{
   UChar link = toUChar((theInstr >> 24) & 1);
   UInt signed_immed_24 = theInstr & 0xFFFFFF;
   UInt branch_offset;
   IRTemp addr = newTemp(Ity_I32);
   IRTemp dest = newTemp(Ity_I32);
   
   if (link) { // LR (R14) = addr of instr after branch instr
      assign( addr, binop(Iop_Add32, getIReg(15), mkU32(4)) );
      putIReg( 14, mkexpr(addr) );
   }
   
   // PC = PC + (SignExtend(signed_immed_24) << 2)
   branch_offset = extend_s_24to32( signed_immed_24 ) << 2;
   assign( dest, binop(Iop_Add32, getIReg(15), mkU32(branch_offset)) );
   
   irbb->jumpkind = link ? Ijk_Call : Ijk_Boring;
   irbb->next     = mkexpr(dest);
   
   // Note: Not actually writing to R15 - let the IR stuff do that.
   
   DIP("b%s%s 0x%x\n",
       link ? "l" : "",
       name_ARMCondcode( (theInstr >> 28) & 0xF ),
       branch_offset);
}
















/*------------------------------------------------------------*/
/*--- Disassemble a single instruction                     ---*/
/*------------------------------------------------------------*/

/* Disassemble a single instruction into IR.  The instruction
   is located in host memory at &guest_code[delta].
   Set *size to be the size of the instruction.
   If the returned value is Dis_Resteer,
   the next guest address is assigned to *whereNext.  If resteerOK
   is False, disInstr may not return Dis_Resteer. */
   
static DisResult disInstr ( /*IN*/  Bool    resteerOK,
                            /*IN*/  Bool    (*resteerOkFn) ( Addr64 ),
                            /*IN*/  Long    delta, 
                            /*OUT*/ Int*    size,
                            /*OUT*/ Addr64* whereNext )
{
   //   IRType    ty;
   //  IRTemp    addr, t1, t2;
   //   Int       alen;
   UChar opc1, opc2, opc_tmp; //, modrm, abyte;
   ARMCondcode cond;
   //  UInt      d32;
   // UChar     dis_buf[50];
   // Int       am_sz, d_sz;
   DisResult whatNext = Dis_Continue;
   UInt      theInstr;
   

   /* At least this is simple on ARM: insns are all 4 bytes long, and
      4-aligned.  So just fish the whole thing out of memory right now
      and have done. */
   
   /* We will set *size to 4 if the insn is successfully decoded.
      Setting it to 0 by default makes bbToIR_ARM abort if we fail the
      decode. */
   *size = 0;

   theInstr = *(UInt*)(&guest_code[delta]);

//   vex_printf("START: 0x%x, %,b\n", theInstr, theInstr );

   DIP("\t0x%x:  ", toUInt(guest_pc_bbstart+delta));



   // TODO: fix the client-request stuff, else nothing will work

   /* Spot the client-request magic sequence. */
   // Essentially a v. unlikely sequence of noops that we can catch
   {
      UInt* code = (UInt*)(guest_code + delta);
      
      /* Spot this:                                       
         E1A00EE0                   mov  r0, r0, ror #29
         E1A001E0                   mov  r0, r0, ror #3
         E1A00DE0                   mov  r0, r0, ror #27
         E1A002E0                   mov  r0, r0, ror #5
         E1A006E0                   mov  r0, r0, ror #13
         E1A009E0                   mov  r0, r0, ror #19
      */
      /* I suspect these will have to be turned the other way round to
         work on little-endian arm. */
      if (code[0] == 0xE1A00EE0 &&
          code[1] == 0xE1A001E0 &&
          code[2] == 0xE1A00DE0 &&
          code[3] == 0xE1A002E0 &&
          code[4] == 0xE1A006E0 &&
          code[5] == 0xE1A009E0) {

         // uh ... I'll figure this out later.  possibly r0 = client_request(r0) */
         DIP("?CAB? = client_request ( ?CAB? )\n");
         
         *size = 24;
         
         irbb->next     = mkU32(toUInt(guest_pc_bbstart+delta));
         irbb->jumpkind = Ijk_ClientReq;
         
         whatNext = Dis_StopHere;
         goto decode_success;
      }
   }





   /*
     Deal with condition first
   */
   cond = (theInstr >> 28) & 0xF;    /* opcode: bits 31:28 */
//   vex_printf("\ndisInstr(arm): cond: 0x%x, %b\n", cond, cond );
   
   switch (cond) {
   case 0xF:   // => Illegal instruction prior to v5 (see ARM ARM A3-5)
      vex_printf("disInstr(arm): illegal condition\n");
      goto decode_failure;
      
   case 0xE:   // => Unconditional: go translate the instruction
      break;

   default:
      // => Valid condition: translate the condition test first
      stmt( IRStmt_Exit( mk_armg_calculate_condition(cond),
                         Ijk_Boring,
                         IRConst_U32(toUInt(guest_pc_bbstart+delta+4)) ) );
      //irbb->next     = mkU32(guest_pc_bbstart+delta+4);
      //irbb->jumpkind = Ijk_Boring;
   }
   


   /* Primary opcode is roughly bits 27:20 (ARM ARM(v2) A3-2)
      secondary opcode is bits 4:0 */
   opc1 = toUChar((theInstr >> 20) & 0xFF);    /* opcode1: bits 27:20 */
   opc2 = toUChar((theInstr >> 4 ) & 0xF);     /* opcode2: bits 7:4   */
//   vex_printf("disInstr(arm): opcode1: 0x%2x, %,09b\n", opc1, opc1 );
//   vex_printf("disInstr(arm): opcode2: 0x%02x, %,04b\n", opc2, opc2 );
   
   switch (opc1 >> 4) { // instr[27:24]
   case 0x0:
   case 0x1:
      /*
        Multiplies, extra load/store instructions: ARM ARM A3-3
      */
      if ( (opc1 & 0xE0) == 0x0 && (opc2 & 0x9) == 0x9 ) {  // 000xxxxx && 1xx1
         if (opc2 == 0x9) {
            if ((opc1 & 0x1C) == 0x00) {  // multiply (accumulate)
               goto decode_failure;
            }
            if ((opc1 & 0x18) == 0x08) {  // multiply (accumulate) long
               goto decode_failure;
            }
            if ((opc1 & 0x1B) == 0x10) {  // swap/swap byte
               goto decode_failure;
            }
         }
         if ( opc2 == 0xB ) {
            if ((opc1 & 0x04) == 0x00) {  // load/store 1/2word reg offset
               goto decode_failure;
            } else {                      // load/store 1/2word imm offset
               goto decode_failure;
            }
         }
         if ((opc2 & 0xD) == 0xD) {
            if ((opc1 & 0x05) == 0x00) {  // load/store 2 words reg offset
               goto decode_failure;
            }
            if ((opc1 & 0x05) == 0x04) {  // load/store 2 words imm offset
               goto decode_failure;
            }
            if ((opc1 & 0x05) == 0x01) {  // load/store signed 1/2word/byte reg offset
               goto decode_failure;
            }
            if ((opc1 & 0x05) == 0x05) {  // load/store signed 1/2word/byte imm offset
               goto decode_failure;
            }
         }
      } /* endif: Multiplies, extra load/store... */
      
      /* 
         'Misc' Instructions: ARM ARM A3-4
      */
      if ((opc1 & 0xF9) == 0x10) {  // 0001 0xx0
         opc_tmp = toUChar((opc1 >> 1) & 0x3);
         switch (opc2) {
         case 0x0:
            if ((opc_tmp & 0x1) == 0x0) { // move stat reg -> reg
               goto decode_failure;
            } else {                      // move reg -> stat reg
               goto decode_failure;
            }
            
         case 0x1:
            if (opc_tmp == 0x1) {       // branch/exchange instr set
               goto decode_failure;
            }
            if (opc_tmp == 0x3) {       // count leading zeros
               goto decode_failure;
            }
            break;
            
         case 0x3:
            if (opc_tmp == 0x1) {       // branch & link/exchange instr set
               goto decode_failure;
            }
            break;
            
         case 0x5:                       // enhanced dsp add/subtracts
            goto decode_failure;
            
         case 0x7:
            if (opc_tmp == 0x1) {       // software breakpoint
               if (cond != 0xE) {            // Unpredictable - ARM ARM A3-4
                  vex_printf("disInstr(arm): Unpredictable instruction\n");
                  goto decode_failure;
               }
               goto decode_failure;
            }
            break;
            
         case 0x8: case 0x9: case 0xA:   // enhanced dsp multiplies
         case 0xB: case 0xC: case 0xD: case 0xE:
            goto decode_failure;
            
             default: break;
         }
      } /* endif: 'Misc' Instructions... */
      // fall through...
      
   case 0x2:
   case 0x3:
      if ((opc1 & 0xFB) == 0x30) goto decode_failure; // Undefined - ARM ARM A3-2
      
      /*
        A lonely 'MOV imm to status reg':
      */
      if ((opc1 & 0xFB) == 0x32) { // 0011 0x10
         goto decode_failure;
      }
      
      /*
        Data Processing Instructions
        (if we get here, it's a dpi)
      */
      if (!dis_dataproc( theInstr )) { goto decode_failure; }
      break;
      

   /*
     Load/Store word | unsigned byte
   */
   case 0x6: case 0x7:  // LOAD/STORE reg offset
      if ((opc2 & 0x1) == 0x1) goto decode_failure; // Undefined - ARM ARM A3-2

  case 0x4: case 0x5:  // LOAD/STORE imm offset
     if (!dis_loadstore_w_ub(theInstr)) { goto decode_failure; }
     break;
     
   /* 
      Load/Store multiple 
   */
   case 0x8: case 0x9:
      if (!dis_loadstore_mult(theInstr)) { goto decode_failure; }
      break;
      
      
   /*
     Branch, Branch and Link
   */
   case 0xA: case 0xB:  // B, BL
      // B(L): L=1 => return address stored in link register (R14)
      dis_branch(theInstr);
      whatNext = Dis_StopHere;
      break;
      
      
   /*
     Co-processor instructions
   */
   case 0xC: case 0xD:  // co-pro load/store & double reg trxfrs
      goto decode_failure;
      
   case 0xE:
      if ((opc2 & 0x1) == 0x0) { // co-pro data processing
         goto decode_failure;
      } else {                   // co-pro register transfers
         goto decode_failure;
      }
      
      
   /*
     Software Interrupt
   */
   case 0xF: // swi
      goto decode_failure;
      
   default:
   decode_failure:
   /* All decode failures end up here. */
   vex_printf("disInstr(arm): unhandled instruction: "
              "0x%x\n", theInstr);
   vpanic("armToIR: unimplemented insn");
   
   } /* switch (opc) for the main (primary) opcode switch. */
   
  decode_success:
   /* All decode successes end up here. */
//   vex_printf("disInstr(arm): success");
   DIP("\n");
   
   *size = 4;
   return whatNext;
}

#undef DIP
#undef DIS

/*--------------------------------------------------------------------*/
/*--- end                                         guest-arm/toIR.c ---*/
/*--------------------------------------------------------------------*/
