This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] [ARC] Add support for 48 and 64 bit instructions


This commit adds support for 48 and 64 bit nps400 instructions.

The current arc opcodes library is built around 32-bit instructions,
longer instructions don't fit easily into the current framework.  In
this commit I have attempts to support the longer instructions without
modifying the core opcodes framework too much.

The idea is that all 48 and 64 bit instructions can be thought of as
either 16 or 32 bit instructions plus a 32-bit LIMM extension.  In the
assembler, the incoming instruction is split into a token list in the
normal way, then, when looking up the instruction we find a match in the
long-instructions table.  Once the long instruction match is found we
use the data to encode some of the operands into a single LIMM operand,
which we then place into the token array.  At this point we can then use
the standard assembler core to encode the 16 or 32 bit initial part of
the instruction, and the 32-bit LIMM extension.

The disassembler changes are a little more extensive.  When the
instructions are matched the words are loaded from memory, and an
operand iterator is created.  The diassembler core now uses the operand
iterator to load operands and their values out of the instruction.  The
iterator mechanism knows how to deal with the standard short
instructions, and the new longer instructions.

gas/ChangeLog:

	* config/tc-arc.c (parse_opcode_flags): New function.
	(find_opcode_match): Move flag parsing code out to new function.
	Ignore operands marked IGNORE.
	(build_fake_opcode_hash_entry): New function.
	(find_special_case_long_opcode): New function.
	(find_special_case): Lookup long opcodes.
	* testsuite/gas/arc/nps400-7.d: New file.
	* testsuite/gas/arc/nps400-7.s: New file.

include/ChangeLog:

	* opcode/arc.h (MAX_INSN_ARGS): Increase to 16.
	(struct arc_long_opcode): New structure.
	(arc_long_opcodes): Declare.
	(arc_num_long_opcodes): Declare.

opcodes/ChangeLog:

	* arc-dis.c (struct arc_operand_iterator): New structure.
	(find_format_from_table): All the old content from find_format,
	with some minor adjustments, and parameter renaming.
	(find_format_long_instructions): New function.
	(find_format): Rewritten.
	(arc_insn_length): Add LSB parameter.
	(extract_operand_value): New function.
	(operand_iterator_next): New function.
	(print_insn_arc): Use new functions to find opcode, and iterator
	over operands.
	* arc-opc.c (insert_nps_3bit_dst_short): New function.
	(extract_nps_3bit_dst_short): New function.
	(insert_nps_3bit_src2_short): New function.
	(extract_nps_3bit_src2_short): New function.
	(insert_nps_bitop1_size): New function.
	(extract_nps_bitop1_size): New function.
	(insert_nps_bitop2_size): New function.
	(extract_nps_bitop2_size): New function.
	(insert_nps_bitop_mod4_msb): New function.
	(extract_nps_bitop_mod4_msb): New function.
	(insert_nps_bitop_mod4_lsb): New function.
	(extract_nps_bitop_mod4_lsb): New function.
	(insert_nps_bitop_dst_pos3_pos4): New function.
	(extract_nps_bitop_dst_pos3_pos4): New function.
	(insert_nps_bitop_ins_ext): New function.
	(extract_nps_bitop_ins_ext): New function.
	(arc_operands): Add new operands.
	(arc_long_opcodes): New global array.
	(arc_num_long_opcodes): New global.
	* arc-nps400-tbl.h: Add comments referencing arc_long_opcodes.
---
 gas/ChangeLog                    |  11 +
 gas/config/tc-arc.c              | 263 ++++++++++++++++------
 gas/testsuite/gas/arc/nps400-7.d |  32 +++
 gas/testsuite/gas/arc/nps400-7.s |  41 ++++
 include/ChangeLog                |   7 +
 include/opcode/arc.h             |  26 ++-
 opcodes/ChangeLog                |  33 +++
 opcodes/arc-dis.c                | 469 +++++++++++++++++++++++++++++++--------
 opcodes/arc-nps400-tbl.h         |  15 ++
 opcodes/arc-opc.c                | 298 ++++++++++++++++++++++++-
 10 files changed, 1032 insertions(+), 163 deletions(-)
 create mode 100644 gas/testsuite/gas/arc/nps400-7.d
 create mode 100644 gas/testsuite/gas/arc/nps400-7.s

diff --git a/gas/config/tc-arc.c b/gas/config/tc-arc.c
index 9ec49db..faaa8c2 100644
--- a/gas/config/tc-arc.c
+++ b/gas/config/tc-arc.c
@@ -1549,6 +1549,92 @@ check_cpu_feature (insn_subclass_t sc)
   return TRUE;
 }
 
+/* Parse the flags described by FIRST_PFLAG and NFLGS against the flag
+   operands in OPCODE.  Stores the matching OPCODES into the FIRST_PFLAG
+   array and returns TRUE if the flag operands all match, otherwise,
+   returns FALSE, in which case the FIRST_PFLAG array may have been
+   modified.  */
+
+static bfd_boolean
+parse_opcode_flags (const struct arc_opcode *opcode,
+                    int nflgs,
+                    struct arc_flags *first_pflag)
+{
+  int lnflg, i;
+  const unsigned char *flgidx;
+
+  lnflg = nflgs;
+  for (i = 0; i < nflgs; i++)
+    first_pflag[i].flgp = NULL;
+
+  /* Check the flags.  Iterate over the valid flag classes.  */
+  for (flgidx = opcode->flags; *flgidx; ++flgidx)
+    {
+      /* Get a valid flag class.  */
+      const struct arc_flag_class *cl_flags = &arc_flag_classes[*flgidx];
+      const unsigned *flgopridx;
+      int cl_matches = 0;
+      struct arc_flags *pflag = NULL;
+
+      /* Check for extension conditional codes.  */
+      if (ext_condcode.arc_ext_condcode
+          && cl_flags->class & F_CLASS_EXTEND)
+        {
+          struct arc_flag_operand *pf = ext_condcode.arc_ext_condcode;
+          while (pf->name)
+            {
+              pflag = first_pflag;
+              for (i = 0; i < nflgs; i++, pflag++)
+                {
+                  if (!strcmp (pf->name, pflag->name))
+                    {
+                      if (pflag->flgp != NULL)
+                        return FALSE;
+                      /* Found it.  */
+                      cl_matches++;
+                      pflag->flgp = pf;
+                      lnflg--;
+                      break;
+                    }
+                }
+              pf++;
+            }
+        }
+
+      for (flgopridx = cl_flags->flags; *flgopridx; ++flgopridx)
+        {
+          const struct arc_flag_operand *flg_operand;
+
+          pflag = first_pflag;
+          flg_operand = &arc_flag_operands[*flgopridx];
+          for (i = 0; i < nflgs; i++, pflag++)
+            {
+              /* Match against the parsed flags.  */
+              if (!strcmp (flg_operand->name, pflag->name))
+                {
+                  if (pflag->flgp != NULL)
+                    return FALSE;
+                  cl_matches++;
+                  pflag->flgp = flg_operand;
+                  lnflg--;
+                  break; /* goto next flag class and parsed flag.  */
+                }
+            }
+        }
+
+      if ((cl_flags->class & F_CLASS_REQUIRED) && cl_matches == 0)
+        return FALSE;
+      if ((cl_flags->class & F_CLASS_OPTIONAL) && cl_matches > 1)
+        return FALSE;
+    }
+  /* Did I check all the parsed flags?  */
+  if (lnflg)
+    return FALSE;
+
+  return TRUE;
+}
+
+
 /* Search forward through all variants of an opcode looking for a
    syntax match.  */
 
@@ -1578,8 +1664,7 @@ find_opcode_match (const struct arc_opcode_hash_entry *entry,
        opcode = arc_opcode_hash_entry_iterator_next (entry, &iter))
     {
       const unsigned char *opidx;
-      const unsigned char *flgidx;
-      int tokidx = 0, lnflg, i;
+      int tokidx = 0;
       const expressionS *t = &emptyE;
 
       pr_debug ("%s:%d: find_opcode_match: trying opcode 0x%08X ",
@@ -1755,7 +1840,7 @@ find_opcode_match (const struct arc_opcode_hash_entry *entry,
 			  if (errmsg)
 			    goto match_failed;
 			}
-		      else
+		      else if (!(operand->flags & ARC_OPERAND_IGNORE))
 			goto match_failed;
 		    }
 		  break;
@@ -1832,72 +1917,7 @@ find_opcode_match (const struct arc_opcode_hash_entry *entry,
       pr_debug ("opr ");
 
       /* Setup ready for flag parsing.  */
-      lnflg = nflgs;
-      for (i = 0; i < nflgs; i++)
-	first_pflag[i].flgp = NULL;
-
-      /* Check the flags.  Iterate over the valid flag classes.  */
-      for (flgidx = opcode->flags; *flgidx; ++flgidx)
-	{
-	  /* Get a valid flag class.  */
-	  const struct arc_flag_class *cl_flags = &arc_flag_classes[*flgidx];
-	  const unsigned *flgopridx;
-	  int cl_matches = 0;
-	  struct arc_flags *pflag = NULL;
-
-	  /* Check for extension conditional codes.  */
-	  if (ext_condcode.arc_ext_condcode
-	      && cl_flags->class & F_CLASS_EXTEND)
-	    {
-	      struct arc_flag_operand *pf = ext_condcode.arc_ext_condcode;
-	      while (pf->name)
-		{
-		  pflag = first_pflag;
-		  for (i = 0; i < nflgs; i++, pflag++)
-		    {
-		      if (!strcmp (pf->name, pflag->name))
-			{
-			  if (pflag->flgp != NULL)
-			    goto match_failed;
-			  /* Found it.  */
-			  cl_matches++;
-			  pflag->flgp = pf;
-			  lnflg--;
-			  break;
-			}
-		    }
-		  pf++;
-		}
-	    }
-
-	  for (flgopridx = cl_flags->flags; *flgopridx; ++flgopridx)
-	    {
-	      const struct arc_flag_operand *flg_operand;
-
-	      pflag = first_pflag;
-	      flg_operand = &arc_flag_operands[*flgopridx];
-	      for (i = 0; i < nflgs; i++, pflag++)
-		{
-		  /* Match against the parsed flags.  */
-		  if (!strcmp (flg_operand->name, pflag->name))
-		    {
-		      if (pflag->flgp != NULL)
-			goto match_failed;
-		      cl_matches++;
-		      pflag->flgp = flg_operand;
-		      lnflg--;
-		      break; /* goto next flag class and parsed flag.  */
-		    }
-		}
-	    }
-
-	  if ((cl_flags->class & F_CLASS_REQUIRED) && cl_matches == 0)
-	    goto match_failed;
-	  if ((cl_flags->class & F_CLASS_OPTIONAL) && cl_matches > 1)
-	    goto match_failed;
-	}
-      /* Did I check all the parsed flags?  */
-      if (lnflg)
+      if (!parse_opcode_flags (opcode, nflgs, first_pflag))
 	goto match_failed;
 
       pr_debug ("flg");
@@ -2180,6 +2200,108 @@ find_special_case_flag (const char *opname,
   return NULL;
 }
 
+/* The long instructions are not stored in a hash (there's not many of
+   them) and so there's no arc_opcode_hash_entry structure to return.  This
+   helper function for find_special_case_long_opcode takes an arc_opcode
+   result and places it into a fake arc_opcode_hash_entry that points to
+   the single arc_opcode OPCODE, which is then returned.  */
+
+static const struct arc_opcode_hash_entry *
+build_fake_opcode_hash_entry (const struct arc_opcode *opcode)
+{
+  static struct arc_opcode_hash_entry entry;
+  static struct arc_opcode tmp[2];
+  static const struct arc_opcode *ptr[2];
+
+  memcpy (&tmp[0], opcode, sizeof (struct arc_opcode));
+  memset (&tmp[1], 0, sizeof (struct arc_opcode));
+  entry.count = 1;
+  entry.opcode = ptr;
+  ptr[0] = tmp;
+  ptr[1] = NULL;
+  return &entry;
+}
+
+
+/* Used by the assembler to match the list of tokens against a long (48 or
+   64 bits) instruction.  If a matching long instruction is found, then
+   some of the tokens are consumed in this function and converted into a
+   single LIMM value, which is then added to the end of the token list,
+   where it will be consumed by a LIMM operand that exists in the base
+   opcode of the long instruction.  */
+
+static const struct arc_opcode_hash_entry *
+find_special_case_long_opcode (const char *opname,
+                               int *ntok ATTRIBUTE_UNUSED,
+                               expressionS *tok ATTRIBUTE_UNUSED,
+                               int *nflgs,
+                               struct arc_flags *pflags)
+{
+  unsigned i;
+
+  if (*ntok == MAX_INSN_ARGS)
+    return NULL;
+
+  for (i = 0; i < arc_num_long_opcodes; ++i)
+    {
+      struct arc_opcode fake_opcode;
+      const struct arc_opcode *opcode;
+      struct arc_insn insn;
+      expressionS *limm_token;
+
+      opcode = &arc_long_opcodes[i].base_opcode;
+
+      if (!(opcode->cpu & arc_target))
+        continue;
+
+      if (!check_cpu_feature (opcode->subclass))
+        continue;
+
+      if (strcmp (opname, opcode->name) != 0)
+        continue;
+
+      /* Check that the flags are a match.  */
+      if (!parse_opcode_flags (opcode, *nflgs, pflags))
+        continue;
+
+      /* Parse the LIMM operands into the LIMM template.  */
+      memset (&fake_opcode, 0, sizeof (fake_opcode));
+      fake_opcode.name = "fake limm";
+      fake_opcode.opcode = arc_long_opcodes[i].limm_template;
+      fake_opcode.mask = arc_long_opcodes[i].limm_mask;
+      fake_opcode.cpu = opcode->cpu;
+      fake_opcode.class = opcode->class;
+      fake_opcode.subclass = opcode->subclass;
+      memcpy (&fake_opcode.operands[0],
+              &arc_long_opcodes[i].operands,
+              MAX_INSN_ARGS);
+      /* Leave fake_opcode.flags as zero.  */
+
+      pr_debug ("Calling assemble_insn to build fake limm value\n");
+      assemble_insn (&fake_opcode, tok, *ntok,
+                     NULL, 0, &insn);
+      pr_debug ("   got limm value: 0x%x\n", insn.insn);
+
+      /* Now create a new token at the end of the token array (We know this
+         is safe as the token array is always created with enough space for
+         MAX_INSN_ARGS, and we check at the start at the start of this
+         function that we're not there yet).  This new token will
+         correspond to a LIMM operand that will be contained in the
+         base_opcode of the arc_long_opcode.  */
+      limm_token = &tok[(*ntok)];
+      (*ntok)++;
+
+      /* Modify the LIMM token to hold the constant.  */
+      limm_token->X_op = O_constant;
+      limm_token->X_add_number = insn.insn;
+
+      /* Return the base opcode.  */
+      return build_fake_opcode_hash_entry (opcode);
+    }
+
+    return NULL;
+}
+
 /* Used to find special case opcode.  */
 
 static const struct arc_opcode_hash_entry *
@@ -2196,6 +2318,9 @@ find_special_case (const char *opname,
   if (entry == NULL)
     entry = find_special_case_flag (opname, nflgs, pflags);
 
+  if (entry == NULL)
+    entry = find_special_case_long_opcode (opname, ntok, tok, nflgs, pflags);
+
   return entry;
 }
 
diff --git a/gas/testsuite/gas/arc/nps400-7.d b/gas/testsuite/gas/arc/nps400-7.d
new file mode 100644
index 0000000..8e47d19
--- /dev/null
+++ b/gas/testsuite/gas/arc/nps400-7.d
@@ -0,0 +1,32 @@
+#as: -mcpu=nps400
+#objdump: -dr
+
+.*: +file format .*arc.*
+
+Disassembly of section .text:
+
+[0-9a-f]+ <.*>:
+   0:	5823 0224 10c5      	mrgb	r0,r0,r1,0x4,0x5,0x3,0x8,0x6,0x2
+   6:	5a23 8224 10c5      	mrgb.cl	r2,r2,r1,0x4,0x5,0x3,0x8,0x6,0x2
+   c:	5940 0e04 10c2      	mov2b	r1,r1,r2,0x4,0x3,0x2,0x8,0x1,0x6
+  12:	5940 8e04 10c2      	mov2b.cl	r1,r2,0x4,0x3,0x2,0x8,0x1,0x6
+  18:	5b81 01cf f945      	ext4b	r3,r3,r12,0x1c,0x5,0xa,0x1e,0x1f
+  1e:	5aa1 81cf f945      	ext4b.cl	r2,r13,0x1c,0x5,0xa,0x1e,0x1f
+  24:	5b82 01cf f945      	ins4b	r3,r3,r12,0x5,0xa,0x1e,0x1f,0x1c
+  2a:	5aa2 81cf f945      	ins4b.cl	r2,r13,0x5,0xa,0x1e,0x1f,0x1c
+  30:	5e90 5280 d953 8446 	mov3b	r14,r14,r12,0x1,0,0x6,0x7,0x3,0x2,0x14,0x2,0x15
+  38:	5990 0c60 e281 9201 	mov3b	r1,r1,r12,0x4,0x1,0x1,0x3,0,0x10,0x3,0x3,0x8
+  40:	5e90 39c0 8c34 204e 	mov3b	r14,r14,r12,0x8,0x2,0xe,0x8,0x1,0x2,0xe,0,0x3
+  48:	5990 14a0 b621 b1a1 	mov3b	r1,r1,r12,0xc,0x3,0x1,0x3,0x2,0xd,0x5,0x1,0x2
+  50:	5831 3180 e800 0421 	mov3bcl	r0,r1,0x1,0,0x1,0,0x1,0x1,0xc,0x3,0
+  58:	5831 7fe0 d23b 0845 	mov3bcl	r0,r1,0x2,0x1,0x5,0x16,0x2,0x2,0x1f,0x2,0x3
+  60:	5831 7bc0 bdfb 8cc9 	mov3bcl	r0,r1,0x3,0x2,0x9,0x17,0x3,0x6,0x1e,0x1,0x1f
+  68:	5831 7bc0 87ec 13cb 	mov3bcl	r0,r1,0x4,0x3,0xb,0x18,0,0x1e,0x1e,0,0x1e
+  70:	58d0 38c3 3671 2814 	mov4b	r0,r0,r14,0xa,0x3,0x14,0x2,0x2,0,0x6,0x1,0x7,0xe,0,0x3
+  78:	58d0 e3e2 58b6 048b 	mov4b	r0,r0,r14,0x1,0,0xb,0xc,0x3,0x4,0x1f,0x2,0xb,0x18,0x1,0x2
+  80:	58d0 2068 e3fa 97c8 	mov4b	r0,r0,r14,0x5,0x1,0x8,0x15,0,0x1e,0x3,0x3,0x1f,0x8,0x2,0x8
+  88:	58d0 91a4 8deb 3ecd 	mov4b	r0,r0,r14,0xf,0x2,0xd,0x16,0x1,0x16,0xd,0,0x1e,0x4,0x3,0x4
+  90:	5cb1 1064 7231 0441 	mov4bcl	r12,r13,0x1,0x1,0x1,0x2,0x2,0x2,0x3,0x3,0x3,0x4,0,0x4
+  98:	5dd1 9064 1c31 0441 	mov4bcl	r13,r14,0x1,0x2,0x1,0x2,0x3,0x2,0x3,0,0x3,0x4,0x1,0x4
+  a0:	5a71 1064 a631 0441 	mov4bcl	r2,r3,0x1,0x3,0x1,0x2,0,0x2,0x3,0x1,0x3,0x4,0x2,0x4
+  a8:	5951 9064 c831 0441 	mov4bcl	r1,r2,0x1,0,0x1,0x2,0x1,0x2,0x3,0x2,0x3,0x4,0x3,0x4
diff --git a/gas/testsuite/gas/arc/nps400-7.s b/gas/testsuite/gas/arc/nps400-7.s
new file mode 100644
index 0000000..d02712b
--- /dev/null
+++ b/gas/testsuite/gas/arc/nps400-7.s
@@ -0,0 +1,41 @@
+        .text
+
+        ;; mrgb
+        mrgb		r0,r0,r1,4,5,3,8,6,2
+        mrgb.cl		r2,r2,r1,4,5,3,8,6,2
+
+        ;; mov2b
+        mov2b		r1,r1,r2,4,3,2,8,1,6
+        mov2b.cl	r1,r2,4,3,2,8,1,6
+
+        ;; ext4b
+        ext4b		r3,r3,r12,28,5,10,30,31
+        ext4b.cl	r2,r13,28,5,10,30,31
+
+        ;; ins4b
+        ins4b		r3,r3,r12,5,10,30,31,28
+        ins4b.cl	r2,r13,5,10,30,31,28
+
+        ;; mov3b
+        mov3b   r14,r14,r12, 1,0,6,  7,3,2,  20,2,21
+        mov3b   r1,r1,r12,   4,1,1,  3,0,16, 3,3,8
+        mov3b   r14,r14,r12, 8,2,14, 8,1,2,  14,0,3
+        mov3b   r1,r1,r12,   12,3,1, 3,2,13, 5,1,2
+
+        ;; mov3bcl
+        mov3bcl    r0,r1,  1,0,1,  0,1,1,   12,3,0
+        mov3b.cl   r0,r1,  2,1,5,  22,2,2,  31,2,3
+        mov3bcl    r0,r1,  3,2,9,  23,3,6,  30,1,31
+        mov3b.cl   r0,r1,  4,3,11, 24,0,30, 30,0,30
+
+        ;; mov4b
+        mov4b   r0,r0,r14,  10,3,20, 2,2,0,   6,1,7,   14,0,3
+        mov4b   r0,r0,r14,  1,0,11,  12,3,4,  31,2,11, 24,1,2
+        mov4b   r0,r0,r14,  5,1,8,   21,0,30, 3,3,31,  8,2,8
+        mov4b   r0,r0,r14,  15,2,13, 22,1,22, 13,0,30, 4,3,4
+
+        ;; mov4bl
+        mov4bcl    r12,r13,1,1,1,2,2,2,3,3,3,4,0,4
+        mov4b.cl   r13,r14,1,2,1,2,3,2,3,0,3,4,1,4
+        mov4bcl    r2,r3,  1,3,1,2,0,2,3,1,3,4,2,4
+        mov4b.cl   r1,r2,  1,0,1,2,1,2,3,2,3,4,3,4
diff --git a/include/opcode/arc.h b/include/opcode/arc.h
index d10e311..7a29be7 100644
--- a/include/opcode/arc.h
+++ b/include/opcode/arc.h
@@ -25,7 +25,7 @@
 #define OPCODE_ARC_H
 
 #ifndef MAX_INSN_ARGS
-#define MAX_INSN_ARGS	     8
+#define MAX_INSN_ARGS	     16
 #endif
 
 #ifndef MAX_INSN_FLGS
@@ -132,6 +132,30 @@ struct arc_opcode
   unsigned char flags[MAX_INSN_FLGS + 1];
 };
 
+/* Structure used to describe 48 and 64 bit instructions.  */
+struct arc_long_opcode
+{
+  /* The base instruction is either 16 or 32 bits, and is described like a
+     normal instruction.  */
+  struct arc_opcode base_opcode;
+
+  /* The template value for the 32-bit LIMM extension.  Used by the
+     assembler and disassembler in the same way as the 'opcode' field of
+     'struct arc_opcode'.  */
+  unsigned limm_template;
+
+  /* The mask value for the 32-bit LIMM extension.  Used by the
+     disassembler just like the 'mask' field in 'struct arc_opcode'.  */
+  unsigned limm_mask;
+
+  /* Array of operand codes similar to the 'operands' array in 'struct
+     arc_opcode'.  These operands are used to fill in the LIMM value.  */
+  unsigned char operands[MAX_INSN_ARGS + 1];
+};
+
+extern const struct arc_long_opcode arc_long_opcodes[];
+extern const unsigned arc_num_long_opcodes;
+
 /* The table itself is sorted by major opcode number, and is otherwise
    in the order in which the disassembler should consider
    instructions.  */
diff --git a/opcodes/arc-dis.c b/opcodes/arc-dis.c
index 7757ef6..a39ae28 100644
--- a/opcodes/arc-dis.c
+++ b/opcodes/arc-dis.c
@@ -28,6 +28,43 @@
 #include "arc-dis.h"
 #include "arc-ext.h"
 
+/* Structure used to iterate over, and extract the values for, operands of
+   an opcode.  */
+
+struct arc_operand_iterator
+{
+  enum
+    {
+      OPERAND_ITERATOR_STANDARD,
+      OPERAND_ITERATOR_LONG
+    } mode;
+
+  /* The array of 32-bit values that make up this instruction.  All
+     required values have been pre-loaded into this array during the
+     find_format call.  */
+  unsigned *insn;
+
+  union
+  {
+    struct
+    {
+      /* The opcode this iterator is operating on.  */
+      const struct arc_opcode *opcode;
+
+      /* The index into the opcodes operand index list.  */
+      const unsigned char *opidx;
+    } standard;
+
+    struct
+    {
+      /* The long instruction opcode this iterator is operating on.  */
+      const struct arc_long_opcode *long_opcode;
+
+      /* Two indexes into the opcodes operand index lists.  */
+      const unsigned char *opidx_base, *opidx_limm;
+    } long_insn;
+  } state;
+};
 
 /* Globals variables.  */
 
@@ -102,11 +139,13 @@ special_flag_p (const char *opname,
   return 0;
 }
 
-/* Find proper format for the given opcode.  */
+/* Find opcode from ARC_TABLE given the instruction described by INSN and
+   INSNLEN.  The ISA_MASK restricts the possible matches in ARC_TABLE.  */
+
 static const struct arc_opcode *
-find_format (const struct arc_opcode *arc_table,
-	     unsigned *insn, unsigned int insnLen,
-	     unsigned isa_mask)
+find_format_from_table (const struct arc_opcode *arc_table,
+                        unsigned *insn, unsigned int insn_len,
+                        unsigned isa_mask, bfd_boolean *has_limm)
 {
   unsigned int i = 0;
   const struct arc_opcode *opcode = NULL;
@@ -118,12 +157,12 @@ find_format (const struct arc_opcode *arc_table,
 
     opcode = &arc_table[i++];
 
-    if (ARC_SHORT (opcode->mask) && (insnLen == 2))
+    if (ARC_SHORT (opcode->mask) && (insn_len == 2))
       {
 	if (OPCODE_AC (opcode->opcode) != OPCODE_AC (insn[0]))
 	  continue;
       }
-    else if (!ARC_SHORT (opcode->mask) && (insnLen == 4))
+    else if (!ARC_SHORT (opcode->mask) && (insn_len == 4))
       {
 	if (OPCODE (opcode->opcode) != OPCODE (insn[0]))
 	  continue;
@@ -137,6 +176,8 @@ find_format (const struct arc_opcode *arc_table,
     if (!(opcode->cpu & isa_mask))
       continue;
 
+    *has_limm = FALSE;
+
     /* Possible candidate, check the operands.  */
     for (opidx = opcode->operands; *opidx; opidx++)
       {
@@ -156,13 +197,17 @@ find_format (const struct arc_opcode *arc_table,
 	if (operand->flags & ARC_OPERAND_IR
 	    && !(operand->flags & ARC_OPERAND_LIMM))
 	  {
-	    if ((value == 0x3E && insnLen == 4)
-		|| (value == 0x1E && insnLen == 2))
+	    if ((value == 0x3E && insn_len == 4)
+		|| (value == 0x1E && insn_len == 2))
 	      {
 		invalid = TRUE;
 		break;
 	      }
 	  }
+
+        if (operand->flags & ARC_OPERAND_LIMM
+            && !(operand->flags & ARC_OPERAND_DUPLICATE))
+          *has_limm = TRUE;
       }
 
     /* Check the flags.  */
@@ -210,6 +255,184 @@ find_format (const struct arc_opcode *arc_table,
   return NULL;
 }
 
+/* Find long instructions matching values in INSN array.  */
+
+static const struct arc_long_opcode *
+find_format_long_instructions (unsigned *insn,
+                               unsigned int *insn_len,
+                               unsigned isa_mask,
+                               bfd_vma memaddr,
+                               struct disassemble_info *info)
+{
+  unsigned int i;
+  unsigned limm = 0;
+  bfd_boolean limm_loaded = FALSE;
+
+  for (i = 0; i < arc_num_long_opcodes; ++i)
+    {
+      bfd_byte buffer[4];
+      int status;
+      const struct arc_opcode *opcode;
+
+      opcode = &arc_long_opcodes[i].base_opcode;
+
+      if (ARC_SHORT (opcode->mask) && (*insn_len == 2))
+        {
+          if (OPCODE_AC (opcode->opcode) != OPCODE_AC (insn[0]))
+            continue;
+        }
+      else if (!ARC_SHORT (opcode->mask) && (*insn_len == 4))
+        {
+          if (OPCODE (opcode->opcode) != OPCODE (insn[0]))
+            continue;
+        }
+      else
+        continue;
+
+      if ((insn[0] ^ opcode->opcode) & opcode->mask)
+        continue;
+
+      if (!(opcode->cpu & isa_mask))
+        continue;
+
+      if (!limm_loaded)
+        {
+          status = (*info->read_memory_func) (memaddr + *insn_len, buffer,
+                                              4, info);
+          if (status != 0)
+            return NULL;
+
+          limm = ARRANGE_ENDIAN (info, buffer);
+          limm_loaded = TRUE;
+        }
+
+      /* Check the second word using the mask and template.  */
+      if ((limm & arc_long_opcodes[i].limm_mask)
+          != arc_long_opcodes[i].limm_template)
+        continue;
+
+      (*insn_len) += 4;
+      insn[1] = limm;
+      return &arc_long_opcodes[i];
+    }
+
+  return NULL;
+}
+
+/* Find opcode for INSN, trying various different sources.  The instruction
+   length in INSN_LEN will be updated if the instruction requires a LIMM
+   extension, and the additional values loaded into the INSN array (which
+   must be big enough).
+
+   A pointer to the opcode is placed into OPCODE_RESULT, and ITER is
+   initialised, ready to iterate over the operands of the found opcode.
+
+   This function returns TRUE in almost all cases, FALSE is reserved to
+   indicate an error (failing to find an opcode is not an error) a
+   returned result of FALSE would indicate that the disassembler can't
+   continue.
+
+   If no matching opcode is found then the returned result will be TRUE,
+   the value placed into OPCODE_RESULT will be NULL, ITER will be
+   undefined, and INSN_LEN will be unchanged.
+
+   If a matching opcode is found, then the returned result will be TRUE,
+   the opcode pointer is placed into OPCODE_RESULT, INSN_LEN will be
+   increased by 4 if the instruction requires a LIMM, and the LIMM value
+   will have been loaded into the INSN[1].  Finally, ITER will have been
+   initialised so that calls to OPERAND_ITERATOR_NEXT will iterate over
+   the opcode's operands.  */
+
+static bfd_boolean
+find_format (bfd_vma memaddr, unsigned *insn, unsigned int *insn_len,
+             unsigned isa_mask, struct disassemble_info *info,
+             const struct arc_opcode **opcode_result,
+             struct arc_operand_iterator *iter)
+{
+  const struct arc_opcode *opcode;
+  bfd_boolean needs_limm;
+
+  /* Find the first match in the opcode table.  */
+  opcode = find_format_from_table (arc_opcodes, insn, *insn_len,
+                                   isa_mask, &needs_limm);
+
+  if (opcode == NULL)
+    {
+      const extInstruction_t *einsn;
+
+      /* No instruction found.  Try the extensions.  */
+      einsn = arcExtMap_insn (OPCODE (insn[0]), insn[0]);
+      if (einsn != NULL)
+	{
+	  const char *errmsg = NULL;
+	  opcode = arcExtMap_genOpcode (einsn, isa_mask, &errmsg);
+	  if (opcode == NULL)
+	    {
+	      (*info->fprintf_func) (info->stream,
+				     "An error occured while "
+				     "generating the extension instruction "
+				     "operations");
+              *opcode_result = NULL;
+	      return FALSE;
+	    }
+
+	  opcode = find_format_from_table (opcode, insn, *insn_len,
+                                           isa_mask, &needs_limm);
+	  assert (opcode != NULL);
+	}
+    }
+
+  if (needs_limm && opcode != NULL)
+    {
+      bfd_byte buffer[4];
+      int status;
+
+      status = (*info->read_memory_func) (memaddr + *insn_len, buffer,
+                                          4, info);
+      if (status != 0)
+        {
+          opcode = NULL;
+        }
+      else
+        {
+          insn[1] = ARRANGE_ENDIAN (info, buffer);
+          *insn_len += 4;
+        }
+    }
+
+  if (opcode == NULL)
+    {
+      const struct arc_long_opcode *long_opcode;
+
+      /* No instruction found yet, try the long instructions.  */
+      long_opcode =
+        find_format_long_instructions (insn, insn_len, isa_mask,
+                                       memaddr, info);
+
+      if (long_opcode != NULL)
+        {
+          iter->mode = OPERAND_ITERATOR_LONG;
+          iter->insn = insn;
+          iter->state.long_insn.long_opcode = long_opcode;
+          iter->state.long_insn.opidx_base =
+            long_opcode->base_opcode.operands;
+          iter->state.long_insn.opidx_limm =
+            long_opcode->operands;
+          opcode = &long_opcode->base_opcode;
+        }
+    }
+  else
+    {
+      iter->mode = OPERAND_ITERATOR_STANDARD;
+      iter->insn = insn;
+      iter->state.standard.opcode = opcode;
+      iter->state.standard.opidx = opcode->operands;
+    }
+
+  *opcode_result = opcode;
+  return TRUE;
+}
+
 static void
 print_flags (const struct arc_opcode *opcode,
 	     unsigned *insn,
@@ -319,13 +542,20 @@ get_auxreg (const struct arc_opcode *opcode,
    cached for later use.  */
 
 static unsigned int
-arc_insn_length (bfd_byte msb, struct disassemble_info *info)
+arc_insn_length (bfd_byte msb, bfd_byte lsb, struct disassemble_info *info)
 {
   bfd_byte major_opcode = msb >> 3;
 
   switch (info->mach)
     {
     case bfd_mach_arc_nps400:
+      if (major_opcode == 0xb)
+        {
+          bfd_byte minor_opcode = lsb & 0x1f;
+
+          if (minor_opcode < 4)
+            return 2;
+        }
     case bfd_mach_arc_arc700:
     case bfd_mach_arc_arc600:
       return (major_opcode > 0xb) ? 2 : 4;
@@ -340,6 +570,120 @@ arc_insn_length (bfd_byte msb, struct disassemble_info *info)
     }
 }
 
+/* Extract and return the value of OPERAND from the instruction whose value
+   is held in the array INSN.  */
+
+static int
+extract_operand_value (const struct arc_operand *operand, unsigned *insn)
+{
+  int value;
+
+  /* Read the limm operand, if required.  */
+  if (operand->flags & ARC_OPERAND_LIMM)
+    /* The second part of the instruction value will have been loaded as
+       part of the find_format call made earlier.  */
+    value = insn[1];
+  else
+    {
+      if (operand->extract)
+        value = (*operand->extract) (insn[0], (int *) NULL);
+      else
+        {
+          if (operand->flags & ARC_OPERAND_ALIGNED32)
+            {
+              value = (insn[0] >> operand->shift)
+                & ((1 << (operand->bits - 2)) - 1);
+              value = value << 2;
+            }
+          else
+            {
+              value = (insn[0] >> operand->shift) & ((1 << operand->bits) - 1);
+            }
+          if (operand->flags & ARC_OPERAND_SIGNED)
+            {
+              int signbit = 1 << (operand->bits - 1);
+              value = (value ^ signbit) - signbit;
+            }
+        }
+    }
+
+  return value;
+}
+
+/* Find the next operand, and the operands value from ITER.  Return TRUE if
+   there is another operand, otherwise return FALSE.  If there is an
+   operand returned then the operand is placed into OPERAND, and the value
+   into VALUE.  If there is no operand returned then OPERAND and VALUE are
+   unchanged.  */
+
+static bfd_boolean
+operand_iterator_next (struct arc_operand_iterator *iter,
+                       const struct arc_operand **operand,
+                       int *value)
+{
+  if (iter->mode == OPERAND_ITERATOR_STANDARD)
+    {
+      if (*iter->state.standard.opidx == 0)
+        {
+          *operand = NULL;
+          return FALSE;
+        }
+
+      *operand = &arc_operands[*iter->state.standard.opidx];
+      *value = extract_operand_value (*operand, iter->insn);
+      iter->state.standard.opidx++;
+    }
+  else
+    {
+      const struct arc_operand *operand_base, *operand_limm;
+      int value_base, value_limm;
+
+      if (*iter->state.long_insn.opidx_limm == 0)
+        {
+          *operand = NULL;
+          return FALSE;
+        }
+
+      operand_base = &arc_operands[*iter->state.long_insn.opidx_base];
+      operand_limm = &arc_operands[*iter->state.long_insn.opidx_limm];
+
+      if (operand_base->flags & ARC_OPERAND_LIMM)
+        {
+          /* We've reached the end of the operand list.  */
+          *operand = NULL;
+          return FALSE;
+        }
+
+      value_base = value_limm = 0;
+      if (!(operand_limm->flags & ARC_OPERAND_IGNORE))
+        {
+          /* This should never happen.  If it does then the use of
+             extract_operand_value below will access memory beyond
+             the insn array.  */
+          assert ((operand_limm->flags & ARC_OPERAND_LIMM) == 0);
+
+          *operand = operand_limm;
+          value_limm = extract_operand_value (*operand, &iter->insn[1]);
+        }
+
+      if (!(operand_base->flags & ARC_OPERAND_IGNORE))
+        {
+          *operand = operand_base;
+          value_base = extract_operand_value (*operand, iter->insn);
+        }
+
+      /* This is a bit of a fudge.  There's no reason why simply ORing
+         together the two values is the right thing to do, however, for all
+         the cases we currently have, it is the right thing, so, for now,
+         I've put off solving the more complex problem.  */
+      *value = value_base | value_limm;
+
+      iter->state.long_insn.opidx_base++;
+      iter->state.long_insn.opidx_limm++;
+    }
+  return TRUE;
+}
+
 /* Disassemble ARC instructions.  */
 
 static int
@@ -349,16 +693,18 @@ print_insn_arc (bfd_vma memaddr,
   bfd_byte buffer[4];
   unsigned int lowbyte, highbyte;
   int status;
-  unsigned int insnLen;
+  unsigned int insn_len;
   unsigned insn[2] = { 0, 0 };
   unsigned isa_mask;
-  const unsigned char *opidx;
   const struct arc_opcode *opcode;
-  const extInstruction_t *einsn;
   bfd_boolean need_comma;
   bfd_boolean open_braket;
   int size;
+  const struct arc_operand *operand;
+  int value;
+  struct arc_operand_iterator iter;
 
+  memset (&iter, 0, sizeof (iter));
   lowbyte  = ((info->endian == BFD_ENDIAN_LITTLE) ? 1 : 0);
   highbyte = ((info->endian == BFD_ENDIAN_LITTLE) ? 0 : 1);
 
@@ -453,8 +799,9 @@ print_insn_arc (bfd_vma memaddr,
       return size;
     }
 
-  insnLen = arc_insn_length (buffer[lowbyte], info);
-  switch (insnLen)
+  insn_len = arc_insn_length (buffer[lowbyte], buffer[highbyte], info);
+  pr_debug ("instruction length = %d bytes\n", insn_len);
+  switch (insn_len)
     {
     case 2:
       insn[0] = (buffer[lowbyte] << 8) | buffer[highbyte];
@@ -489,38 +836,18 @@ print_insn_arc (bfd_vma memaddr,
   info->disassembler_needs_relocs = TRUE;
 
   /* Find the first match in the opcode table.  */
-  opcode = find_format (arc_opcodes, insn, insnLen, isa_mask);
+  if (!find_format (memaddr, insn, &insn_len, isa_mask, info, &opcode, &iter))
+    return -1;
 
   if (!opcode)
     {
-      /* No instruction found.  Try the extensions.  */
-      einsn = arcExtMap_insn (OPCODE (insn[0]), insn[0]);
-      if (einsn)
-	{
-	  const char *errmsg = NULL;
-	  opcode = arcExtMap_genOpcode (einsn, isa_mask, &errmsg);
-	  if (opcode == NULL)
-	    {
-	      (*info->fprintf_func) (info->stream,
-				     "An error occured while "
-				     "generating the extension instruction "
-				     "operations");
-	      return -1;
-	    }
-
-	  opcode = find_format (opcode, insn, insnLen, isa_mask);
-	  assert (opcode != NULL);
-	}
+      if (insn_len == 2)
+        (*info->fprintf_func) (info->stream, ".long %#04x", insn[0]);
       else
-	{
-	  if (insnLen == 2)
-	    (*info->fprintf_func) (info->stream, ".long %#04x", insn[0]);
-	  else
-	    (*info->fprintf_func) (info->stream, ".long %#08x", insn[0]);
+        (*info->fprintf_func) (info->stream, ".long %#08x", insn[0]);
 
-	  info->insn_type = dis_noninsn;
-	  return insnLen;
-	}
+      info->insn_type = dis_noninsn;
+      return insn_len;
     }
 
   /* Print the mnemonic.  */
@@ -556,11 +883,9 @@ print_insn_arc (bfd_vma memaddr,
   open_braket = FALSE;
 
   /* Now extract and print the operands.  */
-  for (opidx = opcode->operands; *opidx; opidx++)
+  operand = NULL;
+  while (operand_iterator_next (&iter, &operand, &value))
     {
-      const struct arc_operand *operand = &arc_operands[*opidx];
-      int value;
-
       if (open_braket && (operand->flags & ARC_OPERAND_BRAKET))
 	{
 	  (*info->fprintf_func) (info->stream, "]");
@@ -573,30 +898,9 @@ print_insn_arc (bfd_vma memaddr,
 	  && !(operand->flags & ARC_OPERAND_BRAKET))
 	continue;
 
-      if (operand->extract)
-	value = (*operand->extract) (insn[0], (int *) NULL);
-      else
-	{
-	  if (operand->flags & ARC_OPERAND_ALIGNED32)
-	    {
-	      value = (insn[0] >> operand->shift)
-		& ((1 << (operand->bits - 2)) - 1);
-	      value = value << 2;
-	    }
-	  else
-	    {
-	      value = (insn[0] >> operand->shift) & ((1 << operand->bits) - 1);
-	    }
-	  if (operand->flags & ARC_OPERAND_SIGNED)
-	    {
-	      int signbit = 1 << (operand->bits - 1);
-	      value = (value ^ signbit) - signbit;
-	    }
-	}
-
-      if (operand->flags & ARC_OPERAND_IGNORE
-	  && (operand->flags & ARC_OPERAND_IR
-	      && value == -1))
+      if ((operand->flags & ARC_OPERAND_IGNORE)
+	  && (operand->flags & ARC_OPERAND_IR)
+          && value == -1)
 	continue;
 
       if (need_comma)
@@ -610,20 +914,6 @@ print_insn_arc (bfd_vma memaddr,
 	  continue;
 	}
 
-      /* Read the limm operand, if required.  */
-      if (operand->flags & ARC_OPERAND_LIMM
-	  && !(operand->flags & ARC_OPERAND_DUPLICATE))
-	{
-	  status = (*info->read_memory_func) (memaddr + insnLen, buffer,
-					      4, info);
-	  if (status != 0)
-	    {
-	      (*info->memory_error_func) (status, memaddr + insnLen, info);
-	      return -1;
-	    }
-	  insn[1] = ARRANGE_ENDIAN (info, buffer);
-	}
-
       /* Print the operand as directed by the flags.  */
       if (operand->flags & ARC_OPERAND_IR)
 	{
@@ -644,15 +934,15 @@ print_insn_arc (bfd_vma memaddr,
 	}
       else if (operand->flags & ARC_OPERAND_LIMM)
 	{
-	  const char *rname = get_auxreg (opcode, insn[1], isa_mask);
+	  const char *rname = get_auxreg (opcode, value, isa_mask);
 	  if (rname && open_braket)
 	    (*info->fprintf_func) (info->stream, "%s", rname);
 	  else
 	    {
-	      (*info->fprintf_func) (info->stream, "%#x", insn[1]);
+	      (*info->fprintf_func) (info->stream, "%#x", value);
 	      if (info->insn_type == dis_branch
 		  || info->insn_type == dis_jsr)
-		info->target = (bfd_vma) insn[1];
+		info->target = (bfd_vma) value;
 	    }
 	}
       else if (operand->flags & ARC_OPERAND_PCREL)
@@ -691,14 +981,9 @@ print_insn_arc (bfd_vma memaddr,
 	}
 
       need_comma = TRUE;
-
-      /* Adjust insn len.  */
-      if (operand->flags & ARC_OPERAND_LIMM
-	  && !(operand->flags & ARC_OPERAND_DUPLICATE))
-	insnLen += 4;
     }
 
-  return insnLen;
+  return insn_len;
 }
 
 
diff --git a/opcodes/arc-nps400-tbl.h b/opcodes/arc-nps400-tbl.h
index 5716119..4371bf5 100644
--- a/opcodes/arc-nps400-tbl.h
+++ b/opcodes/arc-nps400-tbl.h
@@ -34,6 +34,21 @@
 /* encode1<.f> */
 { "encode1", 0x48048000, 0xf80f8000, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B, NPS_R_SRC2_3B, NPS_BITOP_SRC_POS, NPS_BITOP_SIZE }, { C_NPS_F }},
 
+/* mrgb - 48 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* mrgb.cl - 48 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* mov2b - 48 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* mov2b.cl - 48 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* ext4 - 48 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* ext4.cl - 48 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* ins4 - 48 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* ins4.cl - 48 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* mov3b - 64 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* mov4b - 64 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* mov3bcl - 64 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* mov4bcl - 64 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* mov3b.cl - 64 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+/* mov4b.cl - 64 bit instruction, see arc_long_opcodes in arc-opc.c.  */
+
 /* rflt a,b,c   00111bbb00101110FBBBCCCCCCAAAAAA */
 { "rflt", 0x382e0000, 0xf8ff8000, ARC_OPCODE_NPS400, BITOP, NONE, { RA, RB, RC }, { 0 }},
 
diff --git a/opcodes/arc-opc.c b/opcodes/arc-opc.c
index bbefb60..75f72c0 100644
--- a/opcodes/arc-opc.c
+++ b/opcodes/arc-opc.c
@@ -682,6 +682,43 @@ extract_nps_3bit_dst (unsigned insn ATTRIBUTE_UNUSED,
 }
 
 static unsigned
+insert_nps_3bit_dst_short (unsigned insn ATTRIBUTE_UNUSED,
+                           int value ATTRIBUTE_UNUSED,
+                           const char **errmsg ATTRIBUTE_UNUSED)
+{
+  switch (value)
+    {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+      insn |= value << 8;
+      break;
+    case 12:
+    case 13:
+    case 14:
+    case 15:
+      insn |= (value - 8) << 8;
+      break;
+    default:
+      *errmsg = _("Register must be either r0-r3 or r12-r15.");
+      break;
+    }
+  return insn;
+}
+
+static int
+extract_nps_3bit_dst_short (unsigned insn ATTRIBUTE_UNUSED,
+                            bfd_boolean * invalid ATTRIBUTE_UNUSED)
+{
+  int value = (insn >> 8) & 0x07;
+  if (value > 3)
+    return (value + 8);
+  else
+    return value;
+}
+
+static unsigned
 insert_nps_3bit_src2 (unsigned insn ATTRIBUTE_UNUSED,
                       int value ATTRIBUTE_UNUSED,
                       const char **errmsg ATTRIBUTE_UNUSED)
@@ -719,6 +756,43 @@ extract_nps_3bit_src2 (unsigned insn ATTRIBUTE_UNUSED,
 }
 
 static unsigned
+insert_nps_3bit_src2_short (unsigned insn ATTRIBUTE_UNUSED,
+                            int value ATTRIBUTE_UNUSED,
+                            const char **errmsg ATTRIBUTE_UNUSED)
+{
+  switch (value)
+    {
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+      insn |= value << 5;
+      break;
+    case 12:
+    case 13:
+    case 14:
+    case 15:
+      insn |= (value - 8) << 5;
+      break;
+    default:
+      *errmsg = _("Register must be either r0-r3 or r12-r15.");
+      break;
+    }
+  return insn;
+}
+
+static int
+extract_nps_3bit_src2_short (unsigned insn ATTRIBUTE_UNUSED,
+                             bfd_boolean * invalid ATTRIBUTE_UNUSED)
+{
+  int value = (insn >> 5) & 0x07;
+  if (value > 3)
+    return (value + 8);
+  else
+    return value;
+}
+
+static unsigned
 insert_nps_bitop_size_2b (unsigned insn ATTRIBUTE_UNUSED,
                           int value ATTRIBUTE_UNUSED,
                           const char **errmsg ATTRIBUTE_UNUSED)
@@ -896,6 +970,8 @@ MAKE_SIZE_INSERT_EXTRACT_FUNCS(fxorb,8,32,5,8,5)
 MAKE_SIZE_INSERT_EXTRACT_FUNCS(wxorb,16,32,5,16,5)
 MAKE_SIZE_INSERT_EXTRACT_FUNCS(bitop,1,32,5,1,10)
 MAKE_SIZE_INSERT_EXTRACT_FUNCS(qcmp,1,8,3,1,9)
+MAKE_SIZE_INSERT_EXTRACT_FUNCS(bitop1,1,32,5,1,20)
+MAKE_SIZE_INSERT_EXTRACT_FUNCS(bitop2,1,32,5,1,25)
 
 static int
 extract_nps_qcmp_m3 (unsigned insn ATTRIBUTE_UNUSED,
@@ -967,6 +1043,73 @@ extract_nps_calc_entry_size (unsigned insn ATTRIBUTE_UNUSED,
   return 1 << entry_size;
 }
 
+static unsigned
+insert_nps_bitop_mod4_msb (unsigned insn ATTRIBUTE_UNUSED,
+                           int value ATTRIBUTE_UNUSED,
+                           const char **errmsg ATTRIBUTE_UNUSED)
+{
+  return insn | ((value & 0x2) << 30);
+}
+
+static int
+extract_nps_bitop_mod4_msb (unsigned insn ATTRIBUTE_UNUSED,
+                            bfd_boolean * invalid ATTRIBUTE_UNUSED)
+{
+  return (insn >> 30) & 0x2;
+}
+
+static unsigned
+insert_nps_bitop_mod4_lsb (unsigned insn ATTRIBUTE_UNUSED,
+                           int value ATTRIBUTE_UNUSED,
+                           const char **errmsg ATTRIBUTE_UNUSED)
+{
+  return insn | ((value & 0x1) << 15);
+}
+
+static int
+extract_nps_bitop_mod4_lsb (unsigned insn ATTRIBUTE_UNUSED,
+                            bfd_boolean * invalid ATTRIBUTE_UNUSED)
+{
+  return (insn >> 15) & 0x1;
+}
+
+static unsigned
+insert_nps_bitop_dst_pos3_pos4 (unsigned insn ATTRIBUTE_UNUSED,
+                                int value ATTRIBUTE_UNUSED,
+                                const char **errmsg ATTRIBUTE_UNUSED)
+{
+  return insn | (value << 10) | (value << 5);
+}
+
+static int
+extract_nps_bitop_dst_pos3_pos4 (unsigned insn ATTRIBUTE_UNUSED,
+                                 bfd_boolean * invalid ATTRIBUTE_UNUSED)
+{
+  if (((insn >> 10) & 0x1f) != ((insn >> 5) & 0x1f))
+    *invalid = TRUE;
+  return ((insn >> 5) & 0x1f);
+}
+
+static unsigned
+insert_nps_bitop_ins_ext (unsigned insn ATTRIBUTE_UNUSED,
+                          int value ATTRIBUTE_UNUSED,
+                          const char **errmsg ATTRIBUTE_UNUSED)
+{
+  if (value < 0 || value > 28)
+    *errmsg = _("Value must be in the range 0 to 28");
+  return insn | (value << 20);
+}
+
+static int
+extract_nps_bitop_ins_ext (unsigned insn ATTRIBUTE_UNUSED,
+                           bfd_boolean * invalid ATTRIBUTE_UNUSED)
+{
+  int value = (insn >> 20) & 0x1f;
+  if (value > 28)
+    *invalid = TRUE;
+  return value;
+}
+
 /* Include the generic extract/insert functions.  Order is important
    as some of the functions present in the .h may be disabled via
    defines.  */
@@ -1295,9 +1438,13 @@ const struct arc_operand arc_operands[] =
      index is used to indicate end-of-list.  */
 #define UNUSED		0
   { 0, 0, 0, 0, 0, 0 },
+
+#define IGNORED		(UNUSED + 1)
+  { 0, 0, 0, ARC_OPERAND_IGNORE | ARC_OPERAND_UNSIGNED | ARC_OPERAND_NCHK, 0, 0 },
+
   /* The plain integer register fields.  Used by 32 bit
      instructions.  */
-#define RA		(UNUSED + 1)
+#define RA		(IGNORED + 1)
   { 6, 0, 0, ARC_OPERAND_IR, 0, 0 },
 #define RB		(RA + 1)
   { 6, 12, 0, ARC_OPERAND_IR, insert_rb, extract_rb },
@@ -1682,6 +1829,66 @@ const struct arc_operand arc_operands[] =
 
 #define NPS_CALC_ENTRY_SIZE	(NPS_QCMP_M3 + 1)
   { 0, 0, 0, ARC_OPERAND_UNSIGNED | ARC_OPERAND_NCHK, insert_nps_calc_entry_size, extract_nps_calc_entry_size },
+
+#define NPS_R_DST_3B_SHORT	(NPS_CALC_ENTRY_SIZE + 1)
+  { 3, 8, 0, ARC_OPERAND_IR | ARC_OPERAND_NCHK, insert_nps_3bit_dst_short, extract_nps_3bit_dst_short },
+
+#define NPS_R_SRC1_3B_SHORT	(NPS_R_DST_3B_SHORT + 1)
+  { 3, 8, 0, ARC_OPERAND_IR | ARC_OPERAND_DUPLICATE | ARC_OPERAND_NCHK, insert_nps_3bit_dst_short, extract_nps_3bit_dst_short },
+
+#define NPS_R_SRC2_3B_SHORT	(NPS_R_SRC1_3B_SHORT + 1)
+  { 3, 5, 0, ARC_OPERAND_IR | ARC_OPERAND_NCHK, insert_nps_3bit_src2_short, extract_nps_3bit_src2_short },
+
+#define NPS_BITOP_SIZE2		(NPS_R_SRC2_3B_SHORT + 1)
+  { 5, 25, 0, ARC_OPERAND_UNSIGNED | ARC_OPERAND_NCHK, insert_nps_bitop2_size, extract_nps_bitop2_size },
+
+#define NPS_BITOP_SIZE1		(NPS_BITOP_SIZE2 + 1)
+  { 5, 20, 0, ARC_OPERAND_UNSIGNED | ARC_OPERAND_NCHK, insert_nps_bitop1_size, extract_nps_bitop1_size },
+
+#define NPS_BITOP_DST_POS3_POS4		(NPS_BITOP_SIZE1 + 1)
+  { 5, 0, 0, ARC_OPERAND_UNSIGNED, insert_nps_bitop_dst_pos3_pos4, extract_nps_bitop_dst_pos3_pos4 },
+
+#define NPS_BITOP_DST_POS4		(NPS_BITOP_DST_POS3_POS4 + 1)
+  { 5, 10, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_DST_POS3		(NPS_BITOP_DST_POS4 + 1)
+  { 5, 5, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_DST_POS2		(NPS_BITOP_DST_POS3 + 1)
+  { 5, 15, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_DST_POS1		(NPS_BITOP_DST_POS2 + 1)
+  { 5, 10, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_SRC_POS4		(NPS_BITOP_DST_POS1 + 1)
+  { 5, 0, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_SRC_POS3		(NPS_BITOP_SRC_POS4 + 1)
+  { 5, 20, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_SRC_POS2		(NPS_BITOP_SRC_POS3 + 1)
+  { 5, 5, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_SRC_POS1		(NPS_BITOP_SRC_POS2 + 1)
+  { 5, 0, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_MOD4_MSB		(NPS_BITOP_SRC_POS1 + 1)
+  { 2, 0, 0, ARC_OPERAND_UNSIGNED, insert_nps_bitop_mod4_msb, extract_nps_bitop_mod4_msb },
+
+#define NPS_BITOP_MOD4_LSB		(NPS_BITOP_MOD4_MSB + 1)
+  { 2, 0, 0, ARC_OPERAND_UNSIGNED, insert_nps_bitop_mod4_lsb, extract_nps_bitop_mod4_lsb },
+
+#define NPS_BITOP_MOD3		(NPS_BITOP_MOD4_LSB + 1)
+  { 2, 29, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_MOD2		(NPS_BITOP_MOD3 + 1)
+  { 2, 27, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_MOD1		(NPS_BITOP_MOD2 + 1)
+  { 2, 25, 0, ARC_OPERAND_UNSIGNED, NULL, NULL },
+
+#define NPS_BITOP_INS_EXT	(NPS_BITOP_MOD1 + 1)
+  { 5, 20, 0, ARC_OPERAND_UNSIGNED, insert_nps_bitop_ins_ext, extract_nps_bitop_ins_ext },
 };
 
 const unsigned arc_num_operands = ARRAY_SIZE (arc_operands);
@@ -2053,3 +2260,92 @@ const struct arc_opcode arc_relax_opcodes[] =
 };
 
 const unsigned arc_num_relax_opcodes = ARRAY_SIZE (arc_relax_opcodes);
+
+/* The following instructions are all either 48 or 64 bits long, and
+   require special handling in the assembler and disassembler.
+
+   The first part of each ARC_LONG_OPCODE is the base ARC_OPCODE, this is
+   either the 16 or 32 bit base instruction, and its opcode list will
+   always end in a LIMM.
+
+   The rest of the ARC_LONG_OPCODE describes how to build the LIMM from the
+   instruction operands.  There are therefore two lists of operands for
+   each ARC_LONG_OPCODE, the second list contains operands that are merged
+   into the limm template, in the same way that a standard 32-bit
+   instruction is built.  This generated limm is then added to the list of
+   tokens that is passed to the standard instruction encoder, along with
+   the first list of operands (from the base arc_opcode).
+
+   The first list of operands then, describes how to build the base
+   instruction, and includes the 32-bit limm that was previously generated
+   as the last operand.
+
+   In most cases operands are either encoded into the base instruction or
+   into the limm.  When this happens the operand slot will be filled with
+   an operand identifier in one list, and will be IGNORED in the other
+   list, this special operand value causes the operand to be ignored,
+   without being encoded at this point.
+
+   However, in some cases, an operand is split between the base instruction
+   and the 32-bit limm, in this case the operand slot will be filled in
+   both operand lists (see mov4b for one example of this).   */
+const struct arc_long_opcode arc_long_opcodes[] =
+  {
+    /* mrgb - (48 bit instruction).  */
+    { { "mrgb", 0x5803, 0xf81f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B_SHORT, NPS_R_SRC1_3B_SHORT, NPS_R_SRC2_3B_SHORT, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, LIMM }, { 0 }},
+      0x00000000, 0x80000000, { IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS1, NPS_BITOP_SRC_POS1, NPS_BITOP_SIZE1, NPS_BITOP_DST_POS2, NPS_BITOP_SRC_POS2, NPS_BITOP_SIZE2 }},
+
+    /* mrgb.cl - (48 bit instruction).  */
+    { { "mrgb", 0x5803, 0xf81f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B_SHORT, NPS_R_SRC1_3B_SHORT, NPS_R_SRC2_3B_SHORT, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, LIMM }, { C_NPS_CL }},
+      0x80000000, 0x80000000, { IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS1, NPS_BITOP_SRC_POS1, NPS_BITOP_SIZE1, NPS_BITOP_DST_POS2, NPS_BITOP_SRC_POS2, NPS_BITOP_SIZE2 }},
+
+    /* mov2b - (48 bit instruction).  */
+    { { "mov2b", 0x5800, 0xf81f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B_SHORT, NPS_R_SRC1_3B_SHORT, NPS_R_SRC2_3B_SHORT,  IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, LIMM }, { 0 }},
+      0x00000000, 0x80000000, { IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS1, NPS_BITOP_MOD1, NPS_BITOP_SRC_POS1, NPS_BITOP_DST_POS2, NPS_BITOP_MOD2, NPS_BITOP_SRC_POS2 }},
+
+    /* mov2b.cl - (48 bit instruction).  */
+    { { "mov2b", 0x5800, 0xf81f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B_SHORT, NPS_R_SRC2_3B_SHORT,  IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, LIMM }, { C_NPS_CL }},
+      0x80000000, 0x80000000, { IGNORED, IGNORED, NPS_BITOP_DST_POS1, NPS_BITOP_MOD1, NPS_BITOP_SRC_POS1, NPS_BITOP_DST_POS2, NPS_BITOP_MOD2, NPS_BITOP_SRC_POS2 }},
+
+    /* ext4 - (48 bit instruction).  */
+    { { "ext4b", 0x5801, 0xf81f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B_SHORT, NPS_R_SRC1_3B_SHORT, NPS_R_SRC2_3B_SHORT,  IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, LIMM }, { 0 }},
+      0x00000000, 0x80000000, { IGNORED, IGNORED, IGNORED, NPS_BITOP_INS_EXT, NPS_BITOP_SRC_POS1, NPS_BITOP_SRC_POS2, NPS_BITOP_DST_POS1, NPS_BITOP_DST_POS2 }},
+
+    /* ext4.cl - (48 bit instruction).  */
+    { { "ext4b", 0x5801, 0xf81f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B_SHORT, NPS_R_SRC2_3B_SHORT,  IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, LIMM }, { C_NPS_CL }},
+      0x80000000, 0x80000000, { IGNORED, IGNORED, NPS_BITOP_INS_EXT, NPS_BITOP_SRC_POS1, NPS_BITOP_SRC_POS2, NPS_BITOP_DST_POS1, NPS_BITOP_DST_POS2 }},
+
+    /* ins4 - (48 bit instruction).  */
+    { { "ins4b", 0x5802, 0xf81f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B_SHORT, NPS_R_SRC1_3B_SHORT, NPS_R_SRC2_3B_SHORT,  IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, LIMM }, { 0 }},
+      0x00000000, 0x80000000, { IGNORED, IGNORED, IGNORED, NPS_BITOP_SRC_POS1, NPS_BITOP_SRC_POS2, NPS_BITOP_DST_POS1, NPS_BITOP_DST_POS2, NPS_BITOP_INS_EXT }},
+
+    /* ins4.cl - (48 bit instruction).  */
+    { { "ins4b", 0x5802, 0xf81f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B_SHORT, NPS_R_SRC2_3B_SHORT,  IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, LIMM }, { C_NPS_CL }},
+      0x80000000, 0x80000000, { IGNORED, IGNORED, NPS_BITOP_SRC_POS1, NPS_BITOP_SRC_POS2, NPS_BITOP_DST_POS1, NPS_BITOP_DST_POS2, NPS_BITOP_INS_EXT }},
+
+    /* mov3b - (64 bit instruction).  */
+    { { "mov3b", 0x58100000, 0xf81f801f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B, NPS_R_SRC1_3B, NPS_R_SRC2_3B, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS3_POS4, IGNORED, IGNORED, LIMM }, { 0 }},
+      0x80000000, 0x80000000, { IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS1, NPS_BITOP_MOD1, NPS_BITOP_SRC_POS1, NPS_BITOP_DST_POS2, NPS_BITOP_MOD2, NPS_BITOP_SRC_POS2, IGNORED, NPS_BITOP_MOD3, NPS_BITOP_SRC_POS3 }},
+
+    /* mov4b - (64 bit instruction).  */
+    { { "mov4b", 0x58100000, 0xf81f0000, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B, NPS_R_SRC1_3B, NPS_R_SRC2_3B, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS3, IGNORED, IGNORED, NPS_BITOP_DST_POS4, NPS_BITOP_MOD4_LSB, NPS_BITOP_SRC_POS4, LIMM }, { 0 }},
+      0x00000000, 0x00000000, { IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS1, NPS_BITOP_MOD1, NPS_BITOP_SRC_POS1, NPS_BITOP_DST_POS2, NPS_BITOP_MOD2, NPS_BITOP_SRC_POS2, IGNORED, NPS_BITOP_MOD3, NPS_BITOP_SRC_POS3, IGNORED, NPS_BITOP_MOD4_MSB, IGNORED}},
+
+    /* mov3bcl - (64 bit instruction).  */
+    { { "mov3bcl", 0x58110000, 0xf81f801f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B, NPS_R_SRC2_3B, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS3_POS4, IGNORED, IGNORED, LIMM }, { 0 }},
+      0x80000000, 0x80000000, { IGNORED, IGNORED, NPS_BITOP_DST_POS1, NPS_BITOP_MOD1, NPS_BITOP_SRC_POS1, NPS_BITOP_DST_POS2, NPS_BITOP_MOD2, NPS_BITOP_SRC_POS2, IGNORED, NPS_BITOP_MOD3, NPS_BITOP_SRC_POS3 }},
+
+    /* mov4bcl - (64 bit instruction).  */
+    { { "mov4bcl", 0x58110000, 0xf81f0000, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B, NPS_R_SRC2_3B, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS3, IGNORED, IGNORED, NPS_BITOP_DST_POS4, NPS_BITOP_MOD4_LSB, NPS_BITOP_SRC_POS4, LIMM }, { 0 }},
+      0x00000000, 0x00000000, { IGNORED, IGNORED, NPS_BITOP_DST_POS1, NPS_BITOP_MOD1, NPS_BITOP_SRC_POS1, NPS_BITOP_DST_POS2, NPS_BITOP_MOD2, NPS_BITOP_SRC_POS2, IGNORED, NPS_BITOP_MOD3, NPS_BITOP_SRC_POS3, IGNORED, NPS_BITOP_MOD4_MSB, IGNORED}},
+
+    /* mov3b.cl - (64 bit instruction).  */
+    { { "mov3b", 0x58110000, 0xf81f801f, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B, NPS_R_SRC2_3B, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS3_POS4, IGNORED, IGNORED, LIMM }, { C_NPS_CL }},
+      0x80000000, 0x80000000, { IGNORED, IGNORED, NPS_BITOP_DST_POS1, NPS_BITOP_MOD1, NPS_BITOP_SRC_POS1, NPS_BITOP_DST_POS2, NPS_BITOP_MOD2, NPS_BITOP_SRC_POS2, IGNORED, NPS_BITOP_MOD3, NPS_BITOP_SRC_POS3 }},
+
+    /* mov4b.cl - (64 bit instruction).  */
+    { { "mov4b", 0x58110000, 0xf81f0000, ARC_OPCODE_NPS400, BITOP, NONE, { NPS_R_DST_3B, NPS_R_SRC2_3B, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, IGNORED, NPS_BITOP_DST_POS3, IGNORED, IGNORED, NPS_BITOP_DST_POS4, NPS_BITOP_MOD4_LSB, NPS_BITOP_SRC_POS4, LIMM }, { C_NPS_CL }},
+      0x00000000, 0x00000000, { IGNORED, IGNORED, NPS_BITOP_DST_POS1, NPS_BITOP_MOD1, NPS_BITOP_SRC_POS1, NPS_BITOP_DST_POS2, NPS_BITOP_MOD2, NPS_BITOP_SRC_POS2, IGNORED, NPS_BITOP_MOD3, NPS_BITOP_SRC_POS3, IGNORED, NPS_BITOP_MOD4_MSB, IGNORED}},
+};
+
+const unsigned arc_num_long_opcodes = ARRAY_SIZE (arc_long_opcodes);
-- 
2.6.4


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]