This is the mail archive of the binutils@sources.redhat.com 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]

Thumb32 assembler (48/69)


This one's a big Twinkie - making all memory address parsing work via
parse_operands.  There is now just one "address" operand code, and the
encoding functions are responsible for rejecting forms that don't work
with their particular instructions.  And, alas, we lose a few more
diagnostic tests.

With this patch, there are no more encoding functions that do their
own parsing.  Further patches will capitalize on that.

zw

	* config/tc-arm.c (struct arm_it): Add shifted field to operands.
	Expand shift_kind to 3 bits.
	(validate_immediate): Rename encode_immediate_arm.
	(validate_immediate_twopart, validate_offset_imm): Move next to sole
	remaining callers, md_apply_fix3 and subroutines.
	(char_or_fail, comma_or_fail, immediate_or_fail, reg_required_here)
	(reg_or_fail, note_reg_or_fail, reg_nonpc_or_fail)
	(note_reg_nonpc_or_fail, ldst_extend, ldst_extend_v4)
	(ld_mode_required_here, parse_cp_address, parse_thumb_address)
	Delete.
	(reg_list, po_reg_or_fail): Use arm_reg_parse.
	(parse_shift): Don't convert RRX to ROR here.  Reorganize slightly.
	(encode_shift_arm): Convert RRX to ROR here.  Set inst.reloc here
	for immediate shifts.
	(parse_address, encode_addr_mode_common_arm, encode_addr_mode_2_arm)
	(encode_addr_mode_3_arm, move_or_literal_pool)
	: New functions.
	(add_to_lit_pool): Don't compensate for pipeline offset here.
	(OP_CADDR, OP_TADDR): Delete.
	(parse_operands): Handle OP_ADDR, not CADDR or TADDR.
	(do_ldrd, do_ldrex, do_ldst, do_ldstt, do_ldstv4, do_ldsttv4, do_pld):
	Use parse_operands and the appropriate encode_addr_mode function.
	(end_of_line): Fold into sole remaining caller, parse_operands.
	(do_lstc, do_t_lds, do_t_ldst, do_vfp_sp_ldst, do_vfp_dp_ldst)
	(do_fpa_ldst): Use ADDR, not CADDR or TADDR.
	(negate_data_op, md_apply_fix3): Update for function renames.
	(md_apply_fix3 <case BFD_RELOC_ARM_LITERAL>): Share code with
	case BFD_RELOC_ARM_OFFSET_IMM.
	* testsuite/gas/arm/armv1-bad.s: Remove more tests for out-of-range
	immediates.
	* testsuite/gas/arm/armv1-bad.l: Update to match.

===================================================================
Index: gas/testsuite/gas/arm/armv1-bad.l
--- gas/testsuite/gas/arm/armv1-bad.l	(revision 50)
+++ gas/testsuite/gas/arm/armv1-bad.l	(revision 51)
@@ -1,11 +1,9 @@
 [^:]*: Assembler messages:
 [^:]*:4: Error: invalid pseudo operation -- `str r0,=0x00ff0000'
 [^:]*:5: Error: bad expression -- `ldr r0,{r1}'
-[^:]*:6: Error: address offset too large -- `ldr r0,\[r1,#4096\]'
-[^:]*:7: Error: address offset too large -- `ldr r0,\[r1,#-4096\]'
-[^:]*:8: Error: bad instruction `cmpl r0,r0'
-[^:]*:9: Error: selected processor does not support `strh r0,\[r1\]'
+[^:]*:6: Error: bad instruction `cmpl r0,r0'
+[^:]*:7: Error: selected processor does not support `strh r0,\[r1\]'
+[^:]*:8: Warning: writeback of base register is UNPREDICTABLE
+[^:]*:9: Warning: writeback of base register when in register list is UNPREDICTABLE
 [^:]*:10: Warning: writeback of base register is UNPREDICTABLE
-[^:]*:11: Warning: writeback of base register when in register list is UNPREDICTABLE
-[^:]*:12: Warning: writeback of base register is UNPREDICTABLE
-[^:]*:14: Warning: if writeback register is in list, it must be the lowest reg in the list
+[^:]*:12: Warning: if writeback register is in list, it must be the lowest reg in the list
===================================================================
Index: gas/testsuite/gas/arm/armv1-bad.s
--- gas/testsuite/gas/arm/armv1-bad.s	(revision 50)
+++ gas/testsuite/gas/arm/armv1-bad.s	(revision 51)
@@ -3,8 +3,6 @@
 entry:
 	str	r0, =0x00ff0000
 	ldr	r0, {r1}
-	ldr	r0, [r1, #4096]
-	ldr	r0, [r1, #-4096]
 	cmpl	r0, r0
 	strh	r0, [r1]
 	ldmfa	r4!, {r8, r9}^
===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c	(revision 50)
+++ gas/config/tc-arm.c	(revision 51)
@@ -196,14 +196,15 @@
     unsigned reg;
     unsigned imm;
     unsigned present    : 1;  /* operand present */
-    unsigned isreg	   : 1;  /* operand was a register */
+    unsigned isreg	: 1;  /* operand was a register */
     unsigned immisreg   : 1;  /* .imm field is a second register */
     unsigned hasreloc   : 1;  /* operand has relocation suffix */
     unsigned writeback  : 1;  /* operand has trailing ! */
     unsigned preind     : 1;  /* preindexed address */
     unsigned postind    : 1;  /* postindexed address */
     unsigned negative   : 1;  /* index register was negated */
-    unsigned shift_kind : 2;  /* shift operation (enum shift_kind, not RRX) */
+    unsigned shifted    : 1;  /* shift applied to operation */
+    unsigned shift_kind : 3;  /* shift operation (enum shift_kind) */
   } operands[6];
 };
 
@@ -595,13 +596,6 @@
 
 #define skip_whitespace(str)  do { if (*(str) == ' ') ++(str); } while (0)
 
-static void
-end_of_line (char * str)
-{
-  if (*str != '\0' && !inst.error)
-    inst.error = _("garbage following instruction");
-}
-
 static inline int
 skip_past_char (char ** str, char c)
 {
@@ -613,18 +607,7 @@
   else
     return FAIL;
 }
-
-#define char_or_fail(str, c)			\
-do {						\
-  if (skip_past_char (str, c) == FAIL)		\
-    {						\
-      inst.error = BAD_ARGS;			\
-      return;					\
-    }						\
- } while (0)
-
 #define skip_past_comma(str) skip_past_char (str, ',')
-#define comma_or_fail(str) char_or_fail(str, ',')
 
 /* Arithmetic expressions (possibly involving symbols).  */
 
@@ -953,12 +936,6 @@
   return SUCCESS;
 }
 
-#define immediate_or_fail(str, var, min, max, popt)			\
-do {									\
-  if (immediate_required_here (str, var, min, max, popt) == FAIL)	\
-    return;								\
- } while (0)
-
 /* As above, but for use in directives, and does not provide
    range checking.  */
 
@@ -986,11 +963,11 @@
 
 #define rotate_left(v, n) (v << n | v >> (32 - n))
 
-/* Check that an immediate is valid.
-   If so, convert it to the right format.  */
+/* If VAL can be encoded in the immediate field of an ARM instruction,
+   return the encoded form.  Otherwise, return FAIL.  */
 
 static unsigned int
-validate_immediate (unsigned int val)
+encode_immediate_arm (unsigned int val)
 {
   unsigned int a;
   unsigned int i;
@@ -1002,52 +979,6 @@
   return FAIL;
 }
 
-/* Check to see if an immediate can be computed as two separate immediate
-   values, added together.  We already know that this value cannot be
-   computed by just one ARM instruction.  */
-
-static unsigned int
-validate_immediate_twopart (unsigned int   val,
-			    unsigned int * highpart)
-{
-  unsigned int a;
-  unsigned int i;
-
-  for (i = 0; i < 32; i += 2)
-    if (((a = rotate_left (val, i)) & 0xff) != 0)
-      {
-	if (a & 0xff00)
-	  {
-	    if (a & ~ 0xffff)
-	      continue;
-	    * highpart = (a  >> 8) | ((i + 24) << 7);
-	  }
-	else if (a & 0xff0000)
-	  {
-	    if (a & 0xff000000)
-	      continue;
-	    * highpart = (a >> 16) | ((i + 16) << 7);
-	  }
-	else
-	  {
-	    assert (a & 0xff000000);
-	    * highpart = (a >> 24) | ((i + 8) << 7);
-	  }
-
-	return (a & 0xff) | (i << 7);
-      }
-
-  return FAIL;
-}
-
-static int
-validate_offset_imm (unsigned int val, int hwse)
-{
-  if ((hwse && val > 255) || val > 4095)
-    return FAIL;
-  return val;
-}
-
 /* Register parsing.  */
 
 /* Generic register parser.  CCP points to what should be the
@@ -1154,67 +1085,6 @@
     }
 }
 
-/* A register of type TYPE is expected at this point.  SHIFT is the
-   place to put it in inst.instruction.  Returns the register number,
-   or FAIL.  */
-
-static int
-reg_required_here (char **str, int shift, enum arm_reg_type type)
-{
-  int reg;
-
-  if ((reg = arm_reg_parse (str, type)) == FAIL)
-    {
-      inst.error = gettext (reg_expected_msgs[type]);
-      return FAIL;
-    }
-
-  if (shift >= 0)
-    {
-      /* VFP SP registers require special handling.  */
-      if (type == REG_TYPE_VFS)
-	vfp_sp_encode_reg (reg, shift);
-      else
-	inst.instruction |= reg << shift;
-    }
-  return reg;
-}
-
-#define reg_or_fail(str, shift, type)			\
-do {							\
-  if (reg_required_here (str, shift, type) == FAIL)	\
-    return;						\
-} while (0)
-
-#define note_reg_or_fail(var, str, shift, type)			\
-do {								\
-  if ((var = reg_required_here (str, shift, type)) == FAIL)	\
-    return;							\
-} while (0)
-
-/* Convenience macros for the common case of needing an ARM register
-   that isn't the PC.  */
-#define reg_nonpc_or_fail(str, shift)			\
-do {							\
-  int reg_;						\
-  note_reg_or_fail (reg_, str, shift, REG_TYPE_RN);	\
-  if (reg_ == REG_PC)					\
-    {							\
-      inst.error = BAD_PC;				\
-      return;						\
-    }							\
-} while (0)
-
-#define note_reg_nonpc_or_fail(var, str, shift)		\
-do {							\
-  note_reg_or_fail (var, str, shift, REG_TYPE_RN);	\
-  if (var == REG_PC)					\
-    {							\
-      inst.error = BAD_PC;				\
-      return;						\
-    }							\
-} while (0)
-
 /* Register lists.  */
 
 static long
@@ -1239,8 +1109,11 @@
 	    {
 	      int reg;
 
-	      if ((reg = reg_required_here (& str, -1, REG_TYPE_RN)) == FAIL)
-		return FAIL;
+	      if ((reg = arm_reg_parse (&str, REG_TYPE_RN)) == FAIL)
+		{
+		  inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+		  return FAIL;
+		}
 
 	      if (in_range)
 		{
@@ -1546,31 +1419,22 @@
     default: abort ();
     }
       
-  if (shift == SHIFT_RRX)
+  if (shift != SHIFT_RRX)
     {
-      inst.operands[i].shift_kind = SHIFT_ROR;
-      *str = p;
-      return SUCCESS;
-    }
+      /* Whitespace can appear here if the next thing is a bare digit.  */
+      skip_whitespace (p);
 
-  /* Whitespace can appear here if the next thing is a bare digit.  */
-  skip_whitespace (p);
-
-  if (mode == NO_SHIFT_RESTRICT
-      && (reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
-    {
-      inst.operands[i].imm = reg;
-      inst.operands[i].immisreg = 1;
-      inst.operands[i].shift_kind = shift;
-      *str = p;
-      return SUCCESS;
+      if (mode == NO_SHIFT_RESTRICT
+	  && (reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+	{
+	  inst.operands[i].imm = reg;
+	  inst.operands[i].immisreg = 1;
+	}
+      else if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+	return FAIL;
     }
-  else if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
-    return FAIL;
-
-  inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
-  inst.reloc.pc_rel = 0;
   inst.operands[i].shift_kind = shift;
+  inst.operands[i].shifted = 1;
   *str = p;
   return SUCCESS;
 }
@@ -1580,19 +1444,21 @@
 static void
 encode_shift_arm (int i)
 {
-  inst.instruction |= inst.operands[i].shift_kind << 5;
-  if (inst.operands[i].immisreg)
+  if (inst.operands[i].shift_kind == SHIFT_RRX)
+    inst.instruction |= SHIFT_ROR << 5;
+  else
     {
-      inst.instruction |= SHIFT_BY_REG;
-      inst.instruction |= inst.operands[i].imm << 8;
+      inst.instruction |= inst.operands[i].shift_kind << 5;
+      if (inst.operands[i].immisreg)
+	{
+	  inst.instruction |= SHIFT_BY_REG;
+	  inst.instruction |= inst.operands[i].imm << 8;
+	}
+      else
+	inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
     }
 }
 
-/* Non-register data operands: reg-or-immediate operands,
-   relocation tags, load/store expressions.  */
-
-
-
 /* Parse a <shifter_operand> for an ARM data processing instruction:
 
       #<immediate>
@@ -1701,221 +1567,178 @@
   return r->reloc;
 }
 
-static int
-ldst_extend (char ** str)
-{
-  int add = INDEX_UP;
+/* Parse all forms of an ARM address expression.  Information is written
+   to inst.operands[i] and/or inst.reloc.
 
-  switch (**str)
-    {
-    case '#':
-    case '$':
-      (*str)++;
-      if (my_get_expression (& inst.reloc.exp, str, GE_NO_PREFIX))
-	return FAIL;
+   Preindexed addressing (.preind=1):
 
-      if (inst.reloc.exp.X_op == O_constant)
-	{
-	  int value = inst.reloc.exp.X_add_number;
+   [Rn, #offset]       .reg=Rn .reloc.exp=offset
+   [Rn, +/-Rm]         .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+   [Rn, +/-Rm, shift]  .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+                       .shift_kind=shift .reloc.exp=shift_imm
 
-	  if (value < -4095 || value > 4095)
-	    {
-	      inst.error = _("address offset too large");
-	      return FAIL;
-	    }
+   These three may have a trailing ! which causes .writeback to be set also.
 
-	  if (value < 0)
-	    {
-	      value = -value;
-	      add = 0;
-	    }
+   Postindexed addressing (.postind=1, .writeback=1):
 
-	  inst.instruction |= add | value;
-	}
-      else
-	{
-	  inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
-	  inst.reloc.pc_rel = 0;
-	}
-      return SUCCESS;
+   [Rn], #offset       .reg=Rn .reloc.exp=offset
+   [Rn], +/-Rm	       .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+   [Rn], +/-Rm, shift  .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+                       .shift_kind=shift .reloc.exp=shift_imm
 
-    case '-':
-      add = 0;
-      /* Fall through.  */
+   Unindexed addressing (.preind=0, .postind=0):
 
-    case '+':
-      (*str)++;
-      /* Fall through.  */
+   [Rn], {option}      .reg=Rn .imm=option .immisreg=0
 
-    default:
-      if (reg_required_here (str, 0, REG_TYPE_RN) == FAIL)
-	return FAIL;
+   Other:
 
-      inst.instruction |= add | OFFSET_REG;
-      if (skip_past_comma (str) == SUCCESS)
+   [Rn]{!}	       shorthand for [Rn,#0]{!}
+   =immediate	       .isreg=0 .reloc.exp=immediate
+   label	       .reg=PC .reloc.pc_rel=1 .reloc.exp=label
+
+  It is the caller's responsibility to check for addressing modes not
+  supported by the instruction, and to set inst.reloc.type.  */
+
+static int
+parse_address (char **str, int i)
+{
+  char *p = *str;
+  int reg;
+
+  if (skip_past_char (&p, '[') == FAIL)
+    {
+      if (skip_past_char (&p, '=') == FAIL)
 	{
-	  if (parse_shift (str, 5, SHIFT_IMMEDIATE)) /* XXX Kludge */
-	    return FAIL;
-	  encode_shift_arm (5);
+	  /* bare address - translate to PC-relative offset */
+	  inst.reloc.pc_rel = 1;
+	  inst.operands[i].reg = REG_PC;
+	  inst.operands[i].isreg = 1;
+	  inst.operands[i].preind = 1;
 	}
+      /* else a load-constant pseudo op, no special treatment needed here */
+
+      if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
+	return FAIL;
+
+      *str = p;
       return SUCCESS;
     }
-}
 
-static int
-ldst_extend_v4 (char ** str)
-{
-  int add = INDEX_UP;
+  if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
+    {
+      inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+      return FAIL;
+    }
+  inst.operands[i].reg = reg;
+  inst.operands[i].isreg = 1;
 
-  switch (**str)
+  if (skip_past_comma (&p) == SUCCESS)
     {
-    case '#':
-    case '$':
-      (*str)++;
-      if (my_get_expression (& inst.reloc.exp, str, GE_NO_PREFIX))
-	return FAIL;
+      inst.operands[i].preind = 1;
 
-      if (inst.reloc.exp.X_op == O_constant)
+      if (*p == '+') p++;
+      else if (*p == '-') p++, inst.operands[i].negative = 1;
+
+      if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
 	{
-	  int value = inst.reloc.exp.X_add_number;
+	  inst.operands[i].imm = reg;
+	  inst.operands[i].immisreg = 1;
 
-	  if (value < -255 || value > 255)
-	    {
-	      inst.error = _("address offset too large");
+	  if (skip_past_comma (&p) == SUCCESS)
+	    if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
 	      return FAIL;
-	    }
-
-	  if (value < 0)
-	    {
-	      value = -value;
-	      add = 0;
-	    }
-
-	  /* Halfword and signextension instructions have the
-             immediate value split across bits 11..8 and bits 3..0.  */
-	  inst.instruction |= (add | HWOFFSET_IMM
-			       | ((value >> 4) << 8) | (value & 0xF));
 	}
       else
 	{
-	  inst.instruction |= HWOFFSET_IMM;
-	  inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
-	  inst.reloc.pc_rel = 0;
+	  if (inst.operands[i].negative)
+	    {
+	      inst.operands[i].negative = 0;
+	      p--;
+	    }
+	  if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+	    return FAIL;
 	}
-      return SUCCESS;
-
-    case '-':
-      add = 0;
-      /* Fall through.  */
-
-    case '+':
-      (*str)++;
-      /* Fall through.  */
-
-    default:
-      if (reg_required_here (str, 0, REG_TYPE_RN) == FAIL)
-	return FAIL;
-
-      inst.instruction |= add;
-      return SUCCESS;
     }
-}
 
-/* Expects **str -> after a comma. May be leading blanks.
-   Advances *str, recognizing a load  mode, and setting inst.instruction.
-   Returns rn, or else FAIL (in which case may set inst.error
-   and not advance str)
-
-   Note: doesn't know Rd, so no err checks that require such knowledge.  */
-
-static int
-ld_mode_required_here (char ** string)
-{
-  char * str = * string;
-  int    rn;
-  int    pre_inc = 0;
-
-  if (* str == '[')
+  if (skip_past_char (&p, ']') == FAIL)
     {
-      str++;
+      inst.error = _("']' expected");
+      return FAIL;
+    }
 
-      if ((rn = reg_required_here (&str, 16, REG_TYPE_RN)) == FAIL)
-	return FAIL;
+  if (skip_past_char (&p, '!') == SUCCESS)
+    inst.operands[i].writeback = 1;
 
-      if (* str == ']')
+  else if (skip_past_comma (&p) == SUCCESS)
+    {
+      if (skip_past_char (&p, '{') == SUCCESS)
 	{
-	  str ++;
+	  /* [Rn], {expr} - unindexed, with option */
+	  if (immediate_required_here (&p, &inst.operands[i].imm,
+				       0, 255, TRUE) == FAIL)
+	    return FAIL;
 
-	  if (skip_past_comma (& str) == SUCCESS)
+	  if (skip_past_char (&p, '}') == FAIL)
 	    {
-	      /* [Rn],... (post inc) */
-	      if (ldst_extend_v4 (&str) == FAIL)
-		return FAIL;
+	      inst.error = _("'}' expected at end of 'option' field");
+	      return FAIL;
 	    }
-	  else 	      /* [Rn] */
+	  if (inst.operands[i].preind)
 	    {
-	      if (* str == '!')
-		{
-		  str ++;
-		  inst.instruction |= WRITE_BACK;
-		}
-
-	      inst.instruction |= INDEX_UP | HWOFFSET_IMM;
-	      pre_inc = 1;
+	      inst.error = _("cannot combine index with option");
+	      return FAIL;
 	    }
+	  *str = p;
+	  return SUCCESS;
 	}
-      else	  /* [Rn,...] */
+      else
 	{
-	  if (skip_past_comma (& str) == FAIL)
+	  inst.operands[i].postind = 1;
+	  inst.operands[i].writeback = 1;
+
+	  if (inst.operands[i].preind)
 	    {
-	      inst.error = _("pre-indexed expression expected");
+	      inst.error = _("cannot combine pre- and post-indexing");
 	      return FAIL;
 	    }
 
-	  pre_inc = 1;
+	  if (*p == '+') p++;
+	  else if (*p == '-') p++, inst.operands[i].negative = 1;
 
-	  if (ldst_extend_v4 (&str) == FAIL)
-	    return FAIL;
-
-	  if (* str ++ != ']')
+	  if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
 	    {
-	      inst.error = _("missing ]");
-	      return FAIL;
-	    }
+	      inst.operands[i].imm = reg;
+	      inst.operands[i].immisreg = 1;
 
-	  if (* str == '!')
+	      if (skip_past_comma (&p) == SUCCESS)
+		if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
+		  return FAIL;
+	    }
+	  else
 	    {
-	      str ++;
-	      inst.instruction |= WRITE_BACK;
+	      if (inst.operands[i].negative)
+		{
+		  inst.operands[i].negative = 0;
+		  p--;
+		}
+	      if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+		return FAIL;
 	    }
 	}
     }
-  else if (* str == '=')	/* ldr's "r,=label" syntax */
-    /* We should never reach here, because <text> = <expression> is
-       caught gas/read.c read_a_source_file() as a .set operation.  */
-    return FAIL;
-  else				/* PC +- 8 bit immediate offset.  */
-    {
-      if (my_get_expression (& inst.reloc.exp, & str, GE_NO_PREFIX))
-	return FAIL;
 
-      inst.instruction            |= HWOFFSET_IMM;	/* The I bit.  */
-      inst.reloc.type              = BFD_RELOC_ARM_OFFSET_IMM8;
-      inst.reloc.exp.X_add_number -= 8;  		/* PC rel adjust.  */
-      inst.reloc.pc_rel            = 1;
-      inst.instruction            |= (REG_PC << 16);
-
-      rn = REG_PC;
-      pre_inc = 1;
+  /* If at this point neither .preind nor .postind is set, we have a
+     bare [Rn]{!}, which is shorthand for [Rn,#0]{!}.  */
+  if (inst.operands[i].preind == 0 && inst.operands[i].postind == 0)
+    {
+      inst.operands[i].preind = 1;
+      inst.reloc.exp.X_op = O_constant;
+      inst.reloc.exp.X_add_number = 0;
     }
-
-  inst.instruction |= (pre_inc ? PRE_INDEX : 0);
-  * string = str;
-
-  return rn;
+  *str = p;
+  return SUCCESS;
 }
 
-
 /* Miscellaneous: PSR flags, endian specifiers, coprocessor
    operands. */
 
@@ -1976,111 +1799,118 @@
   return FAIL;
 }
 
-/* Parse a coprocessor address expression:
-
-      [Rl]{!}	      shorthand for [Rl,#0]{!}
-      [Rl,offset]{!}  +-8 bits, *4 (usually) - preindex, opt. writeback
-      [Rl],offset     +-8 bits, *4 (usually) - postindex
-      [Rl],\{number\} 8 bits - no relocation - "unindexed" (magic)
-      label	      translated to [PC, offset]
-
-   Data is written to inst.operands[i] and/or inst.reloc.exp.
-   Returns SUCCESS or FAIL.  Does not check for no-writeback / no-unindex.  */
-
-static int
-parse_cp_address (char **str, int i)
+/* Subroutine of encode_addr_mode_2_arm and encode_addr_mode_3_arm.  */
+static void
+encode_addr_mode_common_arm (int i, bfd_boolean is_t)
 {
-  char *p = *str;
-  int reg;
-
-  if (skip_past_char (&p, '[') == FAIL)
-    { /* bare address - translate to PC-relative offset */
-      if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
-	return FAIL;
-
-      inst.reloc.pc_rel = 1;
-      inst.reloc.exp.X_add_number -= 8;  /* PC rel adjust.  */
-      inst.operands[i].reg = REG_PC;
-      inst.operands[i].preind = 1;
-      *str = p;
-      return SUCCESS;
-    }
-
-  if ((reg = reg_required_here (&p, -1, REG_TYPE_RN)) == FAIL)
-    return FAIL;
-  inst.operands[i].reg = reg;
-
-  if (skip_past_char (&p, ']') == SUCCESS)
+  assert (inst.operands[i].isreg);
+  inst.instruction |= inst.operands[i].reg << 16;
+  
+  if (inst.operands[i].preind)
     {
-      if (*p == '\0' || *p == '!')  /* [Rn]{!} - shorthand for [Rn,#0]{!} */
+      if (is_t)
 	{
-	  inst.operands[i].preind = 1;
-	  inst.operands[i].writeback = (*p == '!');
-	  inst.reloc.exp.X_op = O_constant;
-	  inst.reloc.exp.X_add_number = 0;
-	  *str = p + (*p == '!');
-	  return SUCCESS;
+	  inst.error = _("instruction does not accept preindexed addressing");
+	  return;
 	}
+      inst.instruction |= PRE_INDEX;
+      if (inst.operands[i].writeback)
+	inst.instruction |= WRITE_BACK;
+      
+    }
+  else if (inst.operands[i].postind)
+    {
+      assert (inst.operands[i].writeback);
+      if (is_t)
+	inst.instruction |= WRITE_BACK;
+    }
+  else /* unindexed - only for coprocessor */
+    {
+      inst.error = _("instruction does not accept unindexed addressing");
+      return;
+    }
+  
+  if (((inst.instruction & WRITE_BACK) || !(inst.instruction & PRE_INDEX))
+      && (((inst.instruction & 0x000f0000) >> 16)
+	  == ((inst.instruction & 0x0000f000) >> 12)))
+    as_warn ((inst.instruction & LOAD_BIT)
+	     ? _("destination register same as write-back base")
+	     : _("source register same as write-back base"));
+}
 
-      if (skip_past_comma (&p) == FAIL)
-	{
-	  inst.error = _("comma expected after closing square bracket");
-	  return FAIL;
-	}
+/* inst.operands[i] was set up by parse_address.  Encode it into an
+   ARM-format mode 2 load or store instruction.  If is_t is true,
+   reject forms that cannot be used with a T instruction (i.e. not
+   post-indexed).  */
+static void
+encode_addr_mode_2_arm (int i, bfd_boolean is_t)
+{
+  encode_addr_mode_common_arm (i, is_t);
 
-      if (skip_past_char (&p, '{') == SUCCESS)
+  if (inst.operands[i].immisreg)
+    {
+      inst.instruction |= INST_IMMEDIATE;  /* yes, this is backwards */
+      inst.instruction |= inst.operands[i].imm << 0;
+      if (!inst.operands[i].negative)
+	inst.instruction |= INDEX_UP;
+      if (inst.operands[i].shifted)
 	{
-	  /* [Rn], {expr} - unindexed, with option */
-	  if (immediate_required_here (&p, &inst.operands[i].imm,
-				       0, 255, TRUE) == FAIL)
-	    return FAIL;
-
-	  if (skip_past_char (&p, '}') == FAIL)
+	  if (inst.operands[i].shift_kind == SHIFT_RRX)
+	    inst.instruction |= SHIFT_ROR << 5;
+	  else
 	    {
-	      inst.error = _("'}' expected at end of 'option' field");
-	      return FAIL;
+	      inst.instruction |= inst.operands[i].shift_kind << 5;
+	      inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
 	    }
 	}
-      else
-	{
-	  if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
-	    return FAIL;
-
-	  inst.operands[i].postind = 1;
-	  inst.operands[i].writeback = 1;
-	}
     }
-  else
+  else /* immediate offset in inst.reloc */
     {
-      /* '['Rn, #expr']'[!]  */
-      if (skip_past_comma (&p) == FAIL)
-	{
-	  inst.error = _("pre-indexed expression expected");
-	  return FAIL;
-	}
+      if (inst.reloc.type == BFD_RELOC_UNUSED)
+	inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+      if (inst.reloc.pc_rel)
+	inst.reloc.exp.X_add_number -= 8;  /* pipeline offset */
+    }
+}
 
-      if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
-	return FAIL;
+/* inst.operands[i] was set up by parse_address.  Encode it into an
+   ARM-format mode 3 load or store instruction.  Reject forms that
+   cannot be used with such instructions.  If is_t is true, reject
+   forms that cannot be used with a T instruction (i.e. not
+   post-indexed).  */
+static void
+encode_addr_mode_3_arm (int i, bfd_boolean is_t)
+{
+  if (inst.operands[i].immisreg && inst.operands[i].shifted)
+    {
+      inst.error = _("instruction does not accept scaled register index");
+      return;
+    }
+  
+  encode_addr_mode_common_arm (i, is_t);
 
-      if (skip_past_char (&p, ']') == FAIL)
-	{
-	  inst.error = _("missing ]");
-	  return FAIL;
-	}
-
-      if (skip_past_char (&p, '!') == SUCCESS)
-	inst.operands[i].writeback = 1;
-
-      inst.operands[i].preind = 1;
+  if (inst.operands[i].immisreg)
+    {
+      inst.instruction |= inst.operands[i].imm << 0;
+      if (!inst.operands[i].negative)
+	inst.instruction |= INDEX_UP;
     }
-  *str = p;
-  return SUCCESS;
+  else /* immediate offset in inst.reloc */
+    {
+      inst.instruction |= HWOFFSET_IMM;
+      if (inst.reloc.type == BFD_RELOC_UNUSED)
+	inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+      if (inst.reloc.pc_rel)
+	inst.reloc.exp.X_add_number -= 8;  /* pipeline offset */
+    }
 }
 
-/* inst.operands[i] was set up by parse_cp_address.  Encode it into an
-   ARM-format instruction.  If wb_ok is false, reject use of writeback;
-   if unind_ok is false, reject use of unindexed addressing.  If
-   reloc_override is not 0, use it instead of BFD_ARM_CP_OFF_IMM.  */
+/* inst.operands[i] was set up by parse_address.  Encode it into an
+   ARM-format instruction.  Reject all forms which cannot be encoded
+   into a coprocessor load/store instruction.  If wb_ok is false,
+   reject use of writeback; if unind_ok is false, reject use of
+   unindexed addressing.  If reloc_override is not 0, use it instead
+   of BFD_ARM_CP_OFF_IMM.  */
 
 static int
 encode_cp_address_arm (int i, int wb_ok, int unind_ok, int reloc_override)
@@ -2124,97 +1954,11 @@
     inst.reloc.type = reloc_override;
   else
     inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+  if (inst.reloc.pc_rel)
+    inst.reloc.exp.X_add_number -= 8;
   return SUCCESS;
 }
 
-/* Parse a Thumb address expression:
-
-      [Rl]
-      [Rl, Rl]
-      [Rl, #imm]     ; 5 bits, *4
-      [SP/PC, #imm]  ; 8 bits, *4
-      =imm           ; pseudo (mov or lit-pool load)
-      label	     ; translated to [PC, offset]
-
-   Data is written to inst.operands[i] and/or inst.reloc.exp.
-   Returns SUCCESS or FAIL.  */
-
-static int
-parse_thumb_address (char **str, int i)
-{
-  char *s = *str;
-  int Rb, Ro;
-
-  if (*s == '[')
-    {
-      s++;
-      if ((Rb = arm_reg_parse (&s, REG_TYPE_RN)) == FAIL)
-	return FAIL;
-      if (Rb > 7 && Rb != REG_SP && Rb != REG_PC)
-	{
-	  inst.error = BAD_HIREG;
-	  return FAIL;
-	}
-      inst.operands[i].reg = Rb;
-      inst.operands[i].isreg = 1;
-      if (skip_past_comma (&s) != FAIL)
-	{
-	  if ((Ro = arm_reg_parse (&s, REG_TYPE_RN)) != FAIL)
-	    {
-	      if (Ro > 7 || Rb > 7)
-		{
-		  inst.error = BAD_HIREG;
-		  return FAIL;
-		}
-	      inst.operands[i].imm = Ro;
-	      inst.operands[i].immisreg = 1;
-	    }
-	  else if (my_get_expression (&inst.reloc.exp, &s, GE_IMM_PREFIX))
-	    return FAIL;
-	}
-      else
-	{
-	  /* [Rd] == [Rd,#0] */
-	  inst.reloc.exp.X_op = O_constant;
-	  inst.reloc.exp.X_add_number = 0;
-	}
-      if (*s++ != ']')
-	{
-	  inst.error = BAD_ARGS;
-	  return FAIL;
-	}
-    }
-  else if (*s == '=')
-    {
-      s++;
-      if (my_get_expression (&inst.reloc.exp, &s, GE_NO_PREFIX))
-	return FAIL;
-
-      if (   inst.reloc.exp.X_op != O_constant
-	  && inst.reloc.exp.X_op != O_symbol)
-	{
-	  inst.error = "Constant expression expected";
-	  return FAIL;
-	}
-    }
-  else
-    {
-      if (my_get_expression (&inst.reloc.exp, &s, GE_NO_PREFIX))
-	return FAIL;
-
-      inst.reloc.exp.X_add_number -= 4; /* Pipeline offset.  */
-      if (inst.reloc.exp.X_op != O_constant)
-	inst.reloc.pc_rel = 1;
-      inst.operands[i].reg = REG_PC;
-      inst.operands[i].isreg = 1;
-    }
-
-  if (!inst.operands[i].immisreg)
-    inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
-  *str = s;
-  return SUCCESS;
-}
-
 /* Parse the flags argument to CPSI[ED].  Returns FAIL on error, or a
    value suitable for splatting into the AIF field of the instruction.  */
 
@@ -3000,12 +2744,87 @@
     }
 
   inst.reloc.exp.X_op         = O_symbol;
-  inst.reloc.exp.X_add_number = ((int) entry) * 4 - 8;
+  inst.reloc.exp.X_add_number = ((int) entry) * 4;
   inst.reloc.exp.X_add_symbol = pool->symbol;
 
   return SUCCESS;
 }
 
+/* inst.reloc.exp describes an "=expr" load pseudo-operation.
+   Determine whether it can be performed with a move instruction; if
+   it can, convert inst.instruction to that move instruction and
+   return 1; if it can't, convert inst.instruction to a literal-pool
+   load and return 0.  If this is not a valid thing to do in the
+   current context, set inst.error and return 1.
+
+   inst.operands[i] describes the destination register.  */
+
+static int
+move_or_literal_pool (int i, bfd_boolean thumb_p, bfd_boolean mode_3)
+{
+  if ((inst.instruction & (thumb_p ? THUMB_LOAD_BIT : LOAD_BIT)) == 0)
+    {
+      inst.error = _("invalid pseudo operation");
+      return 1;
+    }
+  if (inst.reloc.exp.X_op != O_constant && inst.reloc.exp.X_op != O_symbol)
+    {
+      inst.error = _("constant expression expected");
+      return 1;
+    }
+  if (inst.reloc.exp.X_op == O_constant)
+    {
+      if (thumb_p)
+	{
+	  if ((inst.reloc.exp.X_add_number & ~0xFF) == 0)
+	    {
+	      /* This can be done with a mov instruction.  */
+	      inst.instruction  = T_OPCODE_MOV_I8 | (inst.operands[i].reg << 8);
+	      inst.instruction |= inst.reloc.exp.X_add_number;
+	      return 1;
+	    }
+	}
+      else
+	{
+	  int value = encode_immediate_arm (inst.reloc.exp.X_add_number);
+	  if (value != FAIL)
+	    {
+	      /* This can be done with a mov instruction.  */
+	      inst.instruction &= LITERAL_MASK;
+	      inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
+	      inst.instruction |= value & 0xfff;
+	      return 1;
+	    }
+
+	  value = encode_immediate_arm (~inst.reloc.exp.X_add_number);
+	  if (value != FAIL)
+	    {
+	      /* This can be done with a mvn instruction.  */
+	      inst.instruction &= LITERAL_MASK;
+	      inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
+	      inst.instruction |= value & 0xfff;
+	      return 1;
+	    }
+	}
+    }
+
+  if (add_to_lit_pool () == FAIL)
+    {
+      inst.error = _("literal pool insertion failed");
+      return 1;
+    }
+  inst.operands[1].reg = REG_PC;
+  inst.operands[1].isreg = 1;
+  inst.operands[1].preind = 1;
+  inst.reloc.pc_rel = 1;
+  inst.reloc.type = (thumb_p
+		     ? BFD_RELOC_ARM_THUMB_OFFSET
+		     : (mode_3
+			? BFD_RELOC_ARM_HWLITERAL
+			: BFD_RELOC_ARM_LITERAL));
+  return 0;
+}
+
 /* Can't use symbol_new here, so have to create a symbol and then at
    a later date assign it a value. Thats what these functions do.  */
 
@@ -4043,9 +3862,7 @@
 #define OP_VRSLST  064	/* VFP single-precision register list */
 #define OP_VRDLST  065	/* VFP double-precision register list */
 
-#define OP_TADDR   070	/* Thumb memory address expression */
-#define OP_CADDR   071  /* Co-processor memory address expression */
-#define OP_ADDR    072  /* ARM memory address expression (mode 2 or 3) */
+#define OP_ADDR    072  /* Memory address expression (any mode) */
 #define OP_SHOP	   073  /* shifter_operand */
 
 /* This-or-that operands.  All have bit 7 set.  */
@@ -4111,7 +3928,7 @@
 } while (0)
 
 #define po_reg_or_fail(regtype) do {			\
-  val = reg_required_here (&str, -1, regtype);		\
+  val = arm_reg_parse (&str, regtype);			\
   if (val == FAIL)					\
     {							\
       inst.error = _(reg_expected_msgs[regtype]);	\
@@ -4328,16 +4145,11 @@
 	  break;
 
 	  /* Addressing modes */
-	case OP_(TADDR):
-	  if (parse_thumb_address (&str, i))
+	case OP_(ADDR):
+	  if (parse_address (&str, i))
 	    return FAIL;
 	  break;
 
-	case OP_(CADDR):
-	  if (parse_cp_address (&str, i))
-	    return FAIL;
-	  break;
-
 	case OP_(SHOP):
 	  if (parse_shifter_operand (&str, i))
 	    return FAIL;
@@ -4416,7 +4228,10 @@
     }
 
  done:
-  end_of_line (str);
+  /* Check that we have parsed all the arguments.  */
+  if (*str != '\0' && !inst.error)
+    inst.error = _("garbage following instruction");
+
   return inst.error ? FAIL : SUCCESS;
 
  bad_args:
@@ -4866,50 +4681,47 @@
 static void
 do_ldrd (char * str)
 {
-  int rd;
-  int rn;
+  if (parse_operands (str, OPERANDS2(RR,ADDR)))
+    return;
 
-  note_reg_or_fail (rd, &str, 12, REG_TYPE_RN);
-
-  comma_or_fail (&str);
-
-  if ((rn = ld_mode_required_here (& str)) == FAIL)
+  if (!inst.operands[1].isreg)
     {
-      if (!inst.error)
-	inst.error = BAD_ARGS;
+      inst.error = _("'[' expected");
       return;
     }
 
-  /* inst.instruction has now been zapped with Rd and the addressing mode.  */
-  if (rd & 1)		/* Unpredictable result if Rd is odd.  */
+  if (inst.operands[0].reg % 2 != 0)
     {
       inst.error = _("destination register must be even");
       return;
     }
-
-  if (rd == REG_LR)
+  if (inst.operands[0].reg == REG_LR)
     {
       inst.error = _("r14 not allowed here");
       return;
     }
 
-  if (((rd == rn) || (rd + 1 == rn))
-      && ((inst.instruction & WRITE_BACK)
-	  || (!(inst.instruction & PRE_INDEX))))
-    as_warn (_("pre/post-indexing used when modified address register is destination"));
-
-  /* For an index-register load, the index register must not overlap the
-     destination (even if not write-back).  */
-  if ((inst.instruction & V4_STR_BIT) == 0
-      && (inst.instruction & HWOFFSET_IMM) == 0)
+  if (inst.instruction & LOAD_BIT)
     {
-      int rm = inst.instruction & 0x0000000f;
+      /* encode_addr_mode_3_arm will diagnose overlap between the base
+	 register and the first register written; we have to diagnose
+	 overlap between the base and the second register written here.  */
 
-      if (rm == rd || (rm == rd + 1))
-	as_warn (_("ldrd destination registers must not overlap index register"));
+      if ((inst.operands[1].reg == inst.operands[0].reg + 1)
+	  && (inst.operands[1].writeback || inst.operands[1].postind))
+	as_warn (_("base register written back, and overlaps "
+		   "second destination register"));
+
+      /* For an index-register load, the index register must not overlap the
+	 destination (even if not write-back).  */
+      else if (inst.operands[1].immisreg
+	       && (inst.operands[1].imm == inst.operands[0].reg
+		   || inst.operands[1].imm == inst.operands[0].reg + 1))
+	as_warn (_("index register overlaps destination register"));
     }
 
-  end_of_line (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_addr_mode_3_arm (1, /*is_t=*/FALSE);
 }
 
 /* ARM V6 Load Register Exclusive instruction (argument parse).
@@ -4931,220 +4743,40 @@
 static void
 do_ldst (char * str)
 {
-  int pre_inc = 0;
-  int conflict_reg;
-  int value;
+  if (parse_operands (str, OPERANDS2(RR,ADDR)))
+    return;
 
-  note_reg_or_fail (conflict_reg, &str, 12, REG_TYPE_RN);
-  comma_or_fail (&str);
-
-  if (*str == '[')
-    {
-      int reg;
-
-      str++;
-
-      note_reg_or_fail (reg, &str, 16, REG_TYPE_RN);
-
-      /* Conflicts can occur on stores as well as loads.  */
-      conflict_reg = (conflict_reg == reg);
-
-      if (*str == ']')
-	{
-	  str ++;
-
-	  if (skip_past_comma (&str) == SUCCESS)
-	    {
-	      /* [Rn],... (post inc)  */
-	      if (ldst_extend (&str) == FAIL)
-		return;
-	      if (conflict_reg)
-		as_warn ((inst.instruction & LOAD_BIT)
-			  ? _("destination register same as write-back base")
-			  : _("source register same as write-back base"));
-	    }
-	  else
-	    {
-	      /* [Rn]  */
-
-	      if (*str == '!')
-		{
-		  if (conflict_reg)
-		    as_warn ((inst.instruction & LOAD_BIT)
-			     ? _("destination register same as write-back base")
-			     : _("source register same as write-back base"));
-		  str++;
-		  inst.instruction |= WRITE_BACK;
-		}
-
-	      inst.instruction |= INDEX_UP;
-	      pre_inc = 1;
-	    }
-	}
-      else
-	{
-	  /* [Rn,...]  */
-	  comma_or_fail (&str);
-
-	  pre_inc = 1;
-	  if (ldst_extend (&str) == FAIL)
-	    return;
-
-	  if (*str++ != ']')
-	    {
-	      inst.error = _("missing ]");
-	      return;
-	    }
-
-	  if (*str == '!')
-	    {
-	      if (conflict_reg)
-		as_warn ((inst.instruction & LOAD_BIT)
-			  ? _("destination register same as write-back base")
-			  : _("source register same as write-back base"));
-	      str++;
-	      inst.instruction |= WRITE_BACK;
-	    }
-	}
-    }
-  else if (*str == '=')
-    {
-      if ((inst.instruction & LOAD_BIT) == 0)
-	{
-	  inst.error = _("invalid pseudo operation");
-	  return;
-	}
-
-      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op.  */
-      str++;
-
-      expression_or_fail (&inst.reloc.exp, &str, GE_NO_PREFIX);
-
-      if (inst.reloc.exp.X_op != O_constant
-	  && inst.reloc.exp.X_op != O_symbol)
-	{
-	  inst.error = _("constant expression expected");
-	  return;
-	}
-
-      if (inst.reloc.exp.X_op == O_constant)
-	{
-	  value = validate_immediate (inst.reloc.exp.X_add_number);
-
-	  if (value != FAIL)
-	    {
-	      /* This can be done with a mov instruction.  */
-	      inst.instruction &= LITERAL_MASK;
-	      inst.instruction |= (INST_IMMEDIATE
-				   | (OPCODE_MOV << DATA_OP_SHIFT));
-	      inst.instruction |= value & 0xfff;
-	      end_of_line (str);
-	      return;
-	    }
-
-	  value = validate_immediate (~inst.reloc.exp.X_add_number);
-
-	  if (value != FAIL)
-	    {
-	      /* This can be done with a mvn instruction.  */
-	      inst.instruction &= LITERAL_MASK;
-	      inst.instruction |= (INST_IMMEDIATE
-				   | (OPCODE_MVN << DATA_OP_SHIFT));
-	      inst.instruction |= value & 0xfff;
-	      end_of_line (str);
-	      return;
-	    }
-	}
-
-      /* Insert into literal pool.  */
-      if (add_to_lit_pool () == FAIL)
-	{
-	  if (!inst.error)
-	    inst.error = _("literal pool insertion failed");
-	  return;
-	}
-
-      /* Change the instruction exp to point to the pool.  */
-      inst.reloc.type = BFD_RELOC_ARM_LITERAL;
-      inst.reloc.pc_rel = 1;
-      inst.instruction |= (REG_PC << 16);
-      pre_inc = 1;
-    }
-  else
-    {
-      expression_or_fail (&inst.reloc.exp, &str, GE_NO_PREFIX);
-
-      inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
-#ifndef TE_WINCE
-      /* PC rel adjust.  */
-      inst.reloc.exp.X_add_number -= 8;
-#endif
-      inst.reloc.pc_rel = 1;
-      inst.instruction |= (REG_PC << 16);
-      pre_inc = 1;
-    }
-
-  inst.instruction |= (pre_inc ? PRE_INDEX : 0);
-  end_of_line (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  if (!inst.operands[1].isreg)
+    if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/FALSE))
+      return;
+  encode_addr_mode_2_arm (1, /*is_t=*/FALSE);
 }
 
 static void
 do_ldstt (char * str)
 {
-  int conflict_reg;
+  if (parse_operands (str, OPERANDS2(RR,ADDR)))
+    return;
 
-  note_reg_or_fail (conflict_reg, &str, 12, REG_TYPE_RN);
-  comma_or_fail (&str);
-
-  if (*str == '[')
+  /* ldrt/strt always use post-indexed addressing.  Turn [Rn] into [Rn]! and
+     reject [Rn,...].  */
+  if (inst.operands[1].preind)
     {
-      int reg;
-
-      str++;
-
-      note_reg_or_fail (reg, &str, 16, REG_TYPE_RN);
-
-      /* ldrt/strt always use post-indexed addressing, so if the base is
-	 the same as Rd, we warn.  */
-      if (conflict_reg == reg)
-	as_warn ((inst.instruction & LOAD_BIT)
-		 ? _("destination register same as write-back base")
-		 : _("source register same as write-back base"));
-
-      if (*str == ']')
+      if (inst.reloc.exp.X_op == O_constant && inst.reloc.exp.X_add_number == 0)
 	{
-	  str ++;
-
-	  if (skip_past_comma (&str) == SUCCESS)
-	    {
-	      /* [Rn],... (post inc)  */
-	      if (ldst_extend (&str) == FAIL)
-		return;
-	    }
-	  else
-	    {
-	      /* [Rn]  */
-
-	      /* Skip a write-back '!'.  */
-	      if (*str == '!')
-		str++;
-
-	      inst.instruction |= INDEX_UP;
-	    }
+	  inst.operands[1].preind = 0;
+	  inst.operands[1].postind = 1;
+	  inst.operands[1].writeback = 1;
 	}
       else
 	{
-	  inst.error = _("post-indexed expression expected");
+	  inst.error = _("this instruction requires a post-indexed address");
 	  return;
 	}
     }
-  else
-    {
-      inst.error = _("post-indexed expression expected");
-      return;
-    }
-
-  end_of_line (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_addr_mode_2_arm (1, /*is_t=*/TRUE);
 }
 
 /* Halfword and signed-byte load/store operations.  */
@@ -5152,226 +4784,40 @@
 static void
 do_ldstv4 (char * str)
 {
-  int pre_inc = 0;
-  int conflict_reg;
-  int value;
+  if (parse_operands (str, OPERANDS2(RR,ADDR)))
+    return;
 
-  note_reg_or_fail (conflict_reg, &str, 12, REG_TYPE_RN);
-  comma_or_fail (&str);
-
-  if (*str == '[')
-    {
-      int reg;
-
-      str++;
-
-      note_reg_or_fail (reg, &str, 16, REG_TYPE_RN);
-
-      /* Conflicts can occur on stores as well as loads.  */
-      conflict_reg = (conflict_reg == reg);
-
-      if (*str == ']')
-	{
-	  str ++;
-
-	  if (skip_past_comma (&str) == SUCCESS)
-	    {
-	      /* [Rn],... (post inc)  */
-	      if (ldst_extend_v4 (&str) == FAIL)
-		return;
-	      if (conflict_reg)
-		as_warn ((inst.instruction & LOAD_BIT)
-			  ? _("destination register same as write-back base")
-			  : _("source register same as write-back base"));
-	    }
-	  else
-	    {
-	      /* [Rn]  */
-	      inst.instruction |= HWOFFSET_IMM;
-
-	      if (*str == '!')
-		{
-		  if (conflict_reg)
-		    as_warn ((inst.instruction & LOAD_BIT)
-			     ? _("destination register same as write-back base")
-			     : _("source register same as write-back base"));
-		  str++;
-		  inst.instruction |= WRITE_BACK;
-		}
-
-	      inst.instruction |= INDEX_UP;
-	      pre_inc = 1;
-	    }
-	}
-      else
-	{
-	  /* [Rn,...]  */
-	  if (skip_past_comma (&str) == FAIL)
-	    {
-	      inst.error = _("pre-indexed expression expected");
-	      return;
-	    }
-
-	  pre_inc = 1;
-	  if (ldst_extend_v4 (&str) == FAIL)
-	    return;
-
-	  if (*str++ != ']')
-	    {
-	      inst.error = _("missing ]");
-	      return;
-	    }
-
-	  if (*str == '!')
-	    {
-	      if (conflict_reg)
-		as_warn ((inst.instruction & LOAD_BIT)
-			  ? _("destination register same as write-back base")
-			  : _("source register same as write-back base"));
-	      str++;
-	      inst.instruction |= WRITE_BACK;
-	    }
-	}
-    }
-  else if (*str == '=')
-    {
-      if ((inst.instruction & LOAD_BIT) == 0)
-	{
-	  inst.error = _("invalid pseudo operation");
-	  return;
-	}
-
-      /* XXX Does this work correctly for half-word/byte ops?  */
-      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op.  */
-      str++;
-
-      expression_or_fail (&inst.reloc.exp, &str, GE_NO_PREFIX);
-
-      if (inst.reloc.exp.X_op != O_constant
-	  && inst.reloc.exp.X_op != O_symbol)
-	{
-	  inst.error = _("constant expression expected");
-	  return;
-	}
-
-      if (inst.reloc.exp.X_op == O_constant)
-	{
-	  value = validate_immediate (inst.reloc.exp.X_add_number);
-
-	  if (value != FAIL)
-	    {
-	      /* This can be done with a mov instruction.  */
-	      inst.instruction &= LITERAL_MASK;
-	      inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
-	      inst.instruction |= value & 0xfff;
-	      end_of_line (str);
-	      return;
-	    }
-
-	  value = validate_immediate (~ inst.reloc.exp.X_add_number);
-
-	  if (value != FAIL)
-	    {
-	      /* This can be done with a mvn instruction.  */
-	      inst.instruction &= LITERAL_MASK;
-	      inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
-	      inst.instruction |= value & 0xfff;
-	      end_of_line (str);
-	      return;
-	    }
-	}
-
-      /* Insert into literal pool.  */
-      if (add_to_lit_pool () == FAIL)
-	{
-	  if (!inst.error)
-	    inst.error = _("literal pool insertion failed");
-	  return;
-	}
-
-      /* Change the instruction exp to point to the pool.  */
-      inst.instruction |= HWOFFSET_IMM;
-      inst.reloc.type = BFD_RELOC_ARM_HWLITERAL;
-      inst.reloc.pc_rel = 1;
-      inst.instruction |= (REG_PC << 16);
-      pre_inc = 1;
-    }
-  else
-    {
-      expression_or_fail (&inst.reloc.exp, &str, GE_NO_PREFIX);
-
-      inst.instruction |= HWOFFSET_IMM;
-      inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
-#ifndef TE_WINCE
-      /* PC rel adjust.  */
-      inst.reloc.exp.X_add_number -= 8;
-#endif
-      inst.reloc.pc_rel = 1;
-      inst.instruction |= (REG_PC << 16);
-      pre_inc = 1;
-    }
-
-  inst.instruction |= (pre_inc ? PRE_INDEX : 0);
-  end_of_line (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  if (!inst.operands[1].isreg)
+    if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/TRUE))
+      return;
+  encode_addr_mode_3_arm (1, /*is_t=*/FALSE);
 }
 
 static void
 do_ldsttv4 (char * str)
 {
-  int conflict_reg;
+  if (parse_operands (str, OPERANDS2(RR,ADDR)))
+    return;
 
-  note_reg_or_fail (conflict_reg, &str, 12, REG_TYPE_RN);
-  comma_or_fail (&str);
-
-  if (*str == '[')
+  /* ldrt/strt always use post-indexed addressing.  Turn [Rn] into [Rn]! and
+     reject [Rn,...].  */
+  if (inst.operands[1].preind)
     {
-      int reg;
-
-      str++;
-
-      note_reg_or_fail (reg, &str, 16, REG_TYPE_RN);
-
-      /* ldrt/strt always use post-indexed addressing, so if the base is
-	 the same as Rd, we warn.  */
-      if (reg == conflict_reg)
-	as_warn ((inst.instruction & LOAD_BIT)
-		 ? _("destination register same as write-back base")
-		 : _("source register same as write-back base"));
-
-      if (*str == ']')
+      if (inst.reloc.exp.X_op == O_constant && inst.reloc.exp.X_add_number == 0)
 	{
-	  str ++;
-
-	  if (skip_past_comma (&str) == SUCCESS)
-	    {
-	      /* [Rn],... (post inc)  */
-	      if (ldst_extend_v4 (&str) == FAIL)
-		return;
-	    }
-	  else
-	    {
-	      /* [Rn]  */
-
-	      /* Skip a write-back '!'.  */
-	      if (*str == '!')
-		str++;
-
-	      inst.instruction |= (INDEX_UP|HWOFFSET_IMM);
-	    }
+	  inst.operands[1].preind = 0;
+	  inst.operands[1].postind = 1;
+	  inst.operands[1].writeback = 1;
 	}
       else
 	{
-	  inst.error = _("post-indexed expression expected");
+	  inst.error = _("this instruction requires a post-indexed address");
 	  return;
 	}
     }
-  else
-    {
-      inst.error = _("post-indexed expression expected");
-      return;
-    }
-
-  end_of_line (str);
+  inst.instruction |= inst.operands[0].reg << 12;
+  encode_addr_mode_3_arm (1, /*is_t=*/TRUE);
 }
 
 /* Co-processor register load/store.
@@ -5379,7 +4825,7 @@
 static void
 do_lstc (char * str)
 {
-  if (parse_operands (str, OPERANDS3(RCP,RCN,CADDR)))
+  if (parse_operands (str, OPERANDS3(RCP,RCN,ADDR)))
     return;
 
   inst.instruction |= inst.operands[0].reg << 8;
@@ -5581,67 +5027,32 @@
 static void
 do_pld (char * str)
 {
-  int rd;
+  if (parse_operands (str, OPERANDS1(ADDR)))
+    return;
 
-  if (* str != '[')
+  if (!inst.operands[0].isreg)
     {
       inst.error = _("'[' expected after PLD mnemonic");
       return;
     }
-
-  ++str;
-
-  note_reg_or_fail (rd, &str, 16, REG_TYPE_RN);
-
-  if (*str == ']')
+  if (inst.operands[0].postind)
     {
-      /* [Rn], ... ?  */
-      ++str;
-
-      /* Post-indexed addressing is not allowed with PLD.  */
-      if (skip_past_comma (&str) == SUCCESS)
-	{
-	  inst.error
-	    = _("post-indexed expression used in preload instruction");
-	  return;
-	}
-      else if (*str == '!') /* [Rn]! */
-	{
-	  inst.error = _("writeback used in preload instruction");
-	  ++str;
-	}
-      else /* [Rn] */
-	inst.instruction |= INDEX_UP | PRE_INDEX;
+      inst.error = _("post-indexed expression used in preload instruction");
+      return;
     }
-  else /* [Rn, ...] */
+  if (inst.operands[0].writeback)
     {
-      if (skip_past_comma (& str) == FAIL)
-	{
-	  inst.error = _("pre-indexed expression expected");
-	  return;
-	}
-
-      if (ldst_extend (&str) == FAIL)
-	return;
-
-      if (* str != ']')
-	{
-	  inst.error = _("missing ]");
-	  return;
-	}
-
-      ++ str;
-
-      if (* str == '!') /* [Rn]! */
-	{
-	  inst.error = _("writeback used in preload instruction");
-	  ++ str;
-	}
-
-      inst.instruction |= PRE_INDEX;
+      inst.error = _("writeback used in preload instruction");
+      return;
     }
+  if (!inst.operands[0].preind)
+    {
+      inst.error = _("unindexed addressing used in preload instruction");
+      return;
+    }
 
-  end_of_line (str);
+  inst.instruction |= inst.operands[0].reg;
+  encode_addr_mode_2_arm (0, /*is_t=*/FALSE);
 }
 
 /* ARM V5E (El Segundo) saturating-add/subtract (argument parse)
@@ -6211,13 +5622,21 @@
 static void
 do_t_lds (char * str)
 {
-  if (parse_operands (str, OPERANDS2(RL,TADDR)))
+  if (parse_operands (str, OPERANDS2(RL,ADDR)))
     return;
-  if (!inst.operands[1].isreg || !inst.operands[1].immisreg)
+  /* Only [Rn,Rm] is acceptable.  */
+  if (!inst.operands[1].isreg || !inst.operands[1].immisreg
+      || inst.operands[1].postind || inst.operands[1].shifted
+      || inst.operands[1].negative)
     {
       inst.error = _("invalid addressing mode");
       return;
     }
+  if (inst.operands[1].reg > 7 || inst.operands[1].imm > 7)
+    {
+      inst.error = BAD_HIREG;
+      return;
+    }
 
   inst.instruction |= inst.operands[0].reg;
   inst.instruction |= inst.operands[1].reg << 3;
@@ -6227,36 +5646,17 @@
 static void
 do_t_ldst (char *str)
 {
-  if (parse_operands (str, OPERANDS2(RL,TADDR)))
+  if (parse_operands (str, OPERANDS2(RL,ADDR)))
     return;
 
-  if (!inst.operands[1].isreg) /* =expr pseudo */
-    {
-      if (inst.instruction & THUMB_LOAD_BIT)
-	{
-	  inst.error = _("invalid pseudo operation");
-	  return;
-	}
+  if (!inst.operands[1].isreg)
+    if (move_or_literal_pool (0, /*thumb_p=*/TRUE, /*mode_3=*/FALSE))
+      return;
 
-      if (inst.reloc.exp.X_op == O_constant
-	  && ((inst.reloc.exp.X_add_number & ~0xFF) == 0))
-	{
-	  /* This can be done with a mov instruction.  */
-	  inst.instruction  = T_OPCODE_MOV_I8 | (inst.operands[0].reg << 8);
-	  inst.instruction |= inst.reloc.exp.X_add_number;
-	  return;
-	}
-
-      /* Insert into literal pool.  */
-      if (add_to_lit_pool () == FAIL)
-	{
-	  inst.error = _("literal pool insertion failed");
-	  return;
-	}
-
-      inst.reloc.type   = BFD_RELOC_ARM_THUMB_OFFSET;
-      inst.reloc.exp.X_add_number += 4;  /* pipeline offset */
-      inst.operands[1].reg = REG_PC;
+  if (!inst.operands[1].preind || inst.operands[1].shifted)
+    {
+      inst.error = _("Thumb does not support this addressing mode");
+      return;
     }
 
   if (inst.operands[1].reg == REG_PC || inst.operands[1].reg == REG_SP)
@@ -6286,9 +5686,20 @@
 	inst.instruction = T_OPCODE_STR_SP;
 
       inst.instruction |= inst.operands[0].reg << 8;
+      inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+      if (inst.reloc.pc_rel)
+	inst.reloc.exp.X_add_number -= 4;  /* pipeline offset */
+      return;
     }
-  else if (!inst.operands[1].immisreg)
+
+  if (inst.operands[0].reg > 7)
     {
+      inst.error = BAD_HIREG;
+      return;
+    }
+  
+  if (!inst.operands[1].immisreg)
+    {
       /* Immediate offset.  */
       switch (inst.instruction)
 	{
@@ -6303,10 +5714,21 @@
 
       inst.instruction |= inst.operands[0].reg;
       inst.instruction |= inst.operands[1].reg << 3;
+      inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
     }
   else
     {
       /* Register offset.  Opcode is already correct.  */
+      if (inst.operands[1].reg > 7)
+	{
+	  inst.error = BAD_HIREG;
+	  return;
+	}
+      else if (inst.operands[1].negative)
+	{
+	  inst.error = _("Thumb does not support [Rn,-Rm] addressing");
+	  return;
+	}
       inst.instruction |= inst.operands[0].reg;
       inst.instruction |= inst.operands[1].reg << 3;
       inst.instruction |= inst.operands[1].imm << 6;
@@ -6666,7 +6088,7 @@
 static void
 do_vfp_sp_ldst (char * str)
 {
-  if (parse_operands (str, OPERANDS2(RVS,CADDR)))
+  if (parse_operands (str, OPERANDS2(RVS,ADDR)))
     return;
 
   vfp_sp_encode_reg (inst.operands[0].reg, VFP_REG_Sd);
@@ -6676,7 +6098,7 @@
 static void
 do_vfp_dp_ldst (char * str)
 {
-  if (parse_operands (str, OPERANDS2(RVD,CADDR)))
+  if (parse_operands (str, OPERANDS2(RVD,ADDR)))
     return;
 
   inst.instruction |= inst.operands[0].reg << VFP_REG_Dd;
@@ -6832,7 +6254,7 @@
 static void
 do_fpa_ldst (char * str)
 {
-  if (parse_operands (str, OPERANDS2(RF,CADDR)))
+  if (parse_operands (str, OPERANDS2(RF,ADDR)))
     return;
   inst.instruction |= (inst.operands[0].reg << 12);
   encode_cp_address_arm (1, TRUE, TRUE, 0);
@@ -6841,7 +6263,7 @@
 static void
 do_fpa_ldmstm (char * str)
 {
-  if (parse_operands (str, OPERANDS3(RF,bI4,CADDR)))
+  if (parse_operands (str, OPERANDS3(RF,bI4,ADDR)))
     return;
 
   inst.instruction |= (inst.operands[0].reg << 12);
@@ -7032,7 +6454,7 @@
 static void
 do_iwmmxt_wldst (char * str)
 {
-  if (parse_operands (str, OPERANDS2(RIWR,CADDR)))
+  if (parse_operands (str, OPERANDS2(RIWR,ADDR)))
     return;
 
   inst.instruction |= (inst.operands[0].reg << 12);
@@ -7042,7 +6464,7 @@
 static void
 do_iwmmxt_wldstw (char * str)
 {
-  if (parse_operands (str, OPERANDS2(RIWR_RIWC,CADDR)))
+  if (parse_operands (str, OPERANDS2(RIWR_RIWC,ADDR)))
     return;
 
   /* RIWR_RIWC clears .isreg for a control register.  */
@@ -7451,25 +6873,25 @@
 static void
 do_mav_ldst_1 (char * str)
 {
-  do_mav_ldst (str, OPERANDS2(RMF,CADDR));
+  do_mav_ldst (str, OPERANDS2(RMF,ADDR));
 }
 
 static void
 do_mav_ldst_2 (char * str)
 {
-  do_mav_ldst (str, OPERANDS2(RMD,CADDR));
+  do_mav_ldst (str, OPERANDS2(RMD,ADDR));
 }
 
 static void
 do_mav_ldst_3 (char * str)
 {
-  do_mav_ldst (str, OPERANDS2(RMFX,CADDR));
+  do_mav_ldst (str, OPERANDS2(RMFX,ADDR));
 }
 
 static void
 do_mav_ldst_4 (char * str)
 {
-  do_mav_ldst (str, OPERANDS2(RMDX,CADDR));
+  do_mav_ldst (str, OPERANDS2(RMDX,ADDR));
 }
 
 /* XScale instructions.  Also sorted arithmetic before move.  */
@@ -9846,6 +9268,53 @@
   return 0;
 }
 
+/* Subroutine of md_apply_fix3.  Check to see if an immediate can be
+   computed as two separate immediate values, added together.  We
+   already know that this value cannot be computed by just one ARM
+   instruction.  */
+
+static unsigned int
+validate_immediate_twopart (unsigned int   val,
+			    unsigned int * highpart)
+{
+  unsigned int a;
+  unsigned int i;
+
+  for (i = 0; i < 32; i += 2)
+    if (((a = rotate_left (val, i)) & 0xff) != 0)
+      {
+	if (a & 0xff00)
+	  {
+	    if (a & ~ 0xffff)
+	      continue;
+	    * highpart = (a  >> 8) | ((i + 24) << 7);
+	  }
+	else if (a & 0xff0000)
+	  {
+	    if (a & 0xff000000)
+	      continue;
+	    * highpart = (a >> 16) | ((i + 16) << 7);
+	  }
+	else
+	  {
+	    assert (a & 0xff000000);
+	    * highpart = (a >> 24) | ((i + 8) << 7);
+	  }
+
+	return (a & 0xff) | (i << 7);
+      }
+
+  return FAIL;
+}
+
+static int
+validate_offset_imm (unsigned int val, int hwse)
+{
+  if ((hwse && val > 255) || val > 4095)
+    return FAIL;
+  return val;
+}
+
 /* Subroutine of md_apply_fix3.  Do those data_ops which can take a
    negative immediate constant by altering the instruction.  A bit of
    a hack really.
@@ -9864,8 +9333,8 @@
   int op, new_inst;
   unsigned long negated, inverted;
 
-  negated = validate_immediate (-value);
-  inverted = validate_immediate (~value);
+  negated = encode_immediate_arm (-value);
+  inverted = encode_immediate_arm (~value);
 
   op = (*instruction >> DATA_OP_SHIFT) & 0xf;
   switch (op)
@@ -9992,7 +9461,7 @@
 	  break;
 	}
 
-      newimm = validate_immediate (value);
+      newimm = encode_immediate_arm (value);
       temp = md_chars_to_number (buf, INSN_SIZE);
 
       /* If the instruction will fail, see if we can fix things up by
@@ -10015,7 +9484,7 @@
 	unsigned int highpart = 0;
 	unsigned int newinsn  = 0xe1a00000; /* nop.  */
 
-	newimm = validate_immediate (value);
+	newimm = encode_immediate_arm (value);
 	temp = md_chars_to_number (buf, INSN_SIZE);
 
 	/* If the instruction will fail, see if we can fix things up by
@@ -10060,6 +9529,7 @@
       break;
 
     case BFD_RELOC_ARM_OFFSET_IMM:
+    case BFD_RELOC_ARM_LITERAL:
       sign = value >= 0;
 
       if (value < 0)
@@ -10067,9 +9537,13 @@
 
       if (validate_offset_imm (value, 0) == FAIL)
 	{
-	  as_bad_where (fixP->fx_file, fixP->fx_line,
-			_("bad immediate value for offset (%ld)"),
-			(long) value);
+	  if (fixP->fx_r_type == BFD_RELOC_ARM_LITERAL)
+	    as_bad_where (fixP->fx_file, fixP->fx_line,
+			  _("invalid literal constant: pool needs to be closer"));
+	  else
+	    as_bad_where (fixP->fx_file, fixP->fx_line,
+			  _("bad immediate value for offset (%ld)"),
+			  (long) value);
 	  break;
 	}
 
@@ -10103,25 +9577,6 @@
       md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
-    case BFD_RELOC_ARM_LITERAL:
-      sign = value >= 0;
-
-      if (value < 0)
-	value = - value;
-
-      if (validate_offset_imm (value, 0) == FAIL)
-	{
-	  as_bad_where (fixP->fx_file, fixP->fx_line,
-			_("invalid literal constant: pool needs to be closer"));
-	  break;
-	}
-
-      newval = md_chars_to_number (buf, INSN_SIZE);
-      newval &= 0xff7ff000;
-      newval |= value | (sign ? INDEX_UP : 0);
-      md_number_to_chars (buf, newval, INSN_SIZE);
-      break;
-
     case BFD_RELOC_ARM_SHIFT_IMM:
       newval = md_chars_to_number (buf, INSN_SIZE);
       if (((unsigned long) value) > 32

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