This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Thumb32 assembler (25/69)
- From: Zack Weinberg <zack at codesourcery dot com>
- To: binutils <binutils at sourceware dot org>
- Date: Tue, 26 Apr 2005 02:54:36 -0700
- Subject: Thumb32 assembler (25/69)
parse_operands conversion of a bunch of miscellaneous instructions,
notably the CPS family, BKPT, SETEND, and NOP.
zw
* config/tc-arm.c (do_cps_mode): Rename parse_cps_flags. Adjust
interface for use from parse_operands. Simplify internals.
(do_endian_specifier): Similarly.
(OP_CPSF, OP_ENDI, OP_obI31, OP_obIffff, OP_ocI255): New operand parse
codes.
(parse_operands): Handle them. Support instructions with all operands
optional.
(do_bkpt, do_cps, do_cpsi, do_nop, do_t_cps, do_t_setend):
Use parse_operands.
(do_srs): Use immediate_or_fail instead of do_cps_mode.
===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c (revision 26)
+++ gas/config/tc-arm.c (revision 27)
@@ -2516,59 +2516,42 @@
return SUCCESS;
}
-static void
-do_cps_mode (char ** str)
+/* Parse the flags argument to CPSI[ED]. Returns SUCCESS or FAIL as
+ appropriate; on success, *valp gets a value suitable for splatting
+ into the AIF field of the instruction. */
+
+static int
+parse_cps_flags (int *valp, char **str)
{
- int val;
+ int val = 0;
+ int saw_a_flag = 0;
+ char *s = *str;
- /* The mode is a 5 bit field. Valid values are 0-31. */
- if (immediate_required_here (str, &val, 0, 31, TRUE) == FAIL)
- return;
+ for (;;)
+ switch (*s++)
+ {
+ case '\0': case ',':
+ goto done;
- inst.instruction |= val;
-}
+ case 'a': case 'A': saw_a_flag = 1; val |= 0x4; break;
+ case 'i': case 'I': saw_a_flag = 1; val |= 0x2; break;
+ case 'f': case 'F': saw_a_flag = 1; val |= 0x1; break;
-static void
-do_cps_flags (char ** str, int thumb_p)
-{
- struct cps_flag
- {
- char character;
- unsigned long arm_value;
- unsigned long thumb_value;
- };
- static struct cps_flag flag_table[] =
- {
- {'a', 0x100, 0x4 },
- {'i', 0x080, 0x2 },
- {'f', 0x040, 0x1 }
- };
+ default:
+ inst.error = _("unrecognized CPS flag");
+ return FAIL;
+ }
- int saw_a_flag = 0;
-
- /* Get the a, f and i flags. */
- while (**str && **str != ',')
+ done:
+ if (saw_a_flag == 0)
{
- struct cps_flag *p;
- struct cps_flag *q = flag_table + sizeof (flag_table)/sizeof (*p);
-
- for (p = flag_table; p < q; ++p)
- if (strncasecmp (*str, &p->character, 1) == 0)
- {
- inst.instruction |= (thumb_p ? p->thumb_value : p->arm_value);
- saw_a_flag = 1;
- break;
- }
- if (p == q)
- {
- inst.error = _("unrecognized flag");
- return;
- }
- (*str)++;
+ inst.error = _("missing CPS flags");
+ return FAIL;
}
- if (!saw_a_flag)
- inst.error = _("no 'a', 'i', or 'f' flags for 'cps'");
+ *str = s - 1;
+ *valp = val;
+ return SUCCESS;
}
static int
@@ -2593,28 +2576,35 @@
return offset;
}
-/* Returns true if the endian-specifier indicates big-endianness. */
+/* Parse an endian specifier ("BE" or "LE", case insensitive);
+ write 0 into *val for big-endian, 1 for little-endian; return
+ SUCCESS for successful parse, or FAIL and set inst.error. */
static int
-do_endian_specifier (char * str)
+parse_endian_specifier (int *val, char **str)
{
- int big_endian = 0;
+ int little_endian;
+ char *s = *str;
- if (strlen (str) < 2)
- inst.error = _("missing endian specifier");
- else if (strncasecmp (str, "BE", 2) == 0)
+ if (strncasecmp (s, "BE", 2))
+ little_endian = 0;
+ else if (strncasecmp (s, "LE", 2))
+ little_endian = 1;
+ else
{
- str += 2;
- big_endian = 1;
+ inst.error = _("valid endian specifiers are be or le");
+ return FAIL;
}
- else if (strncasecmp (str, "LE", 2) == 0)
- str += 2;
- else
- inst.error = _("valid endian specifiers are be or le");
- end_of_line (str);
+ if (ISALNUM (s[2]) || s[2] == '_')
+ {
+ inst.error = _("valid endian specifiers are be or le");
+ return FAIL;
+ }
- return big_endian;
+ *val = little_endian;
+ *str = s + 2;
+ return SUCCESS;
}
/* Directives: register aliases. */
@@ -4320,10 +4310,19 @@
#define OP_bI7 040 /* immediate, prefix optional, 0 .. 7 */
#define OP_bI15 041 /* 0 .. 15 */
+#define OP_bI31 042 /* 0 .. 31 */
+#define OP_CPSF 060 /* CPS flags */
+#define OP_ENDI 061 /* Endianness specifier */
+
/* Optional operands. All have the high bit set. */
-#define OP_obI7 200 /* optional bI7 */
+#define OP_obI7 200 /* optional, prefix optional, immediate 0 .. 7 */
+#define OP_obI31 201 /* 0 .. 31 */
+#define OP_obIffff 202 /* 0 .. 65535 */
+#define OP_ocI255 210 /* optional, curly-brace enclosed, imm 0 .. 255 */
+
+
/* Macro for referring to one of the above constants as a number.
Should appear solely in parse_operands(). */
#define OP_(x) OP__(OP_##x)
@@ -4377,6 +4376,11 @@
inst.operands[i].imm = imm_; \
} while (0)
+ /* Check for a no-operand instruction, or an instruction with only
+ optional operands given none, and bypass the loop if so. */
+ if (*p == OP_(stop) || ((*p & 0200) && *str == 0))
+ goto done;
+
for (i = 0; ; i++)
{
switch (*p)
@@ -4433,6 +4437,16 @@
case OP_(obI7):
case OP_(bI7): po_imm_or_fail ( 0, 7, TRUE); break;
case OP_(bI15): po_imm_or_fail ( 0, 15, TRUE); break;
+ case OP_(obI31):
+ case OP_(bI31): po_imm_or_fail ( 0, 31, TRUE); break;
+ case OP_(obIffff): po_imm_or_fail (0, 0xffff, TRUE); break;
+
+ case OP_(ocI255):
+ po_char_or_fail ('{');
+ po_imm_or_fail (0, 255, TRUE);
+ po_char_or_fail ('}');
+ inst.operands[i].imm += 1; /* to distinguish 0 from omitted */
+ break;
/* Expressions */
case OP_(iEXP):
@@ -4446,10 +4460,16 @@
break;
/* Misc */
- case OP_(stop):
- /* We only get here for 0 operands. */
- goto done;
+ case OP_(CPSF):
+ if (parse_cps_flags (&inst.operands[i].imm, &str))
+ return FAIL;
+ break;
+ case OP_(ENDI):
+ if (parse_endian_specifier (&inst.operands[i].imm, &str))
+ return FAIL;
+ break;
+
default:
as_fatal ("unhandled operand code %03o", *p);
}
@@ -4674,20 +4694,16 @@
static void
do_bkpt (char * str)
{
- int number = 0;
+ /* As a convenience, we allow BKPT with no immediate (corresponding
+ to BKPT #0). */
+ if (parse_operands (str, OPERANDS1(obIffff)))
+ return;
- /* As a convenience we allow 'bkpt' without an operand. */
- if (is_immediate_prefix (*str) || ISDIGIT (*str))
- if (immediate_required_here (&str, &number, 0, 0xffff, TRUE) == FAIL)
- return;
-
/* Top 12 of 16 bits to bits 19:8. */
- inst.instruction |= (number & 0xfff0) << 4;
+ inst.instruction |= (inst.operands[0].imm & 0xfff0) << 4;
/* Bottom 4 of 16 bits to bits 3:0. */
- inst.instruction |= number & 0xf;
-
- end_of_line (str);
+ inst.instruction |= inst.operands[0].imm & 0xf;
}
static void
@@ -4953,19 +4969,19 @@
static void
do_cps (char * str)
{
- do_cps_mode (&str);
- end_of_line (str);
+ if (parse_operands (str, OPERANDS1(bI31)))
+ return;
+ inst.instruction |= inst.operands[0].imm;
}
static void
do_cpsi (char * str)
{
- do_cps_flags (&str, /*thumb_p=*/0);
+ if (parse_operands (str, OPERANDS2(CPSF,obI31)))
+ return;
- if (skip_past_comma (&str) == SUCCESS)
- do_cps_mode (&str);
-
- end_of_line (str);
+ inst.instruction |= inst.operands[0].imm << 6;
+ inst.instruction |= inst.operands[1].imm;
}
static void
@@ -5749,27 +5765,15 @@
static void
do_nop (char * str)
{
- int hint;
+ if (parse_operands (str, OPERANDS1(ocI255)))
+ return;
- if (*str == '{')
+ if (inst.operands[0].imm)
{
- str++;
- if (immediate_required_here (&str, &hint, 0, 255, TRUE) == FAIL)
- return;
-
- if (*str != '}')
- {
- inst.error = _("'}' expected at end of hint field");
- return;
- }
- str++;
-
/* Architectural NOP hints are CPSR sets with no bits selected. */
inst.instruction &= 0xf0000000;
- inst.instruction |= 0x0320f000 + hint;
+ inst.instruction |= 0x0320f000 + inst.operands[0].imm - 1;
}
-
- end_of_line (str);
}
static void
@@ -6043,7 +6047,10 @@
static void
do_setend (char * str)
{
- if (do_endian_specifier (str))
+ if (parse_operands (str, OPERANDS1(ENDI)))
+ return;
+
+ if (inst.operands[0].imm)
inst.instruction |= 0x200;
}
@@ -6124,12 +6131,16 @@
static void
do_srs (char * str)
{
+ int val;
/* This is necessary because the generic expression parser will
choke on "#16!". */
char *exclam = strchr (str, '!');
if (exclam)
*exclam = '\0';
- do_cps_mode (&str);
+
+ immediate_or_fail (&str, &val, 0, 31, TRUE);
+ inst.instruction |= val;
+
if (exclam)
*exclam = '!';
if (*str == '!')
@@ -7069,8 +7080,10 @@
static void
do_t_cps (char * str)
{
- do_cps_flags (&str, /*thumb_p=*/1);
- end_of_line (str);
+ if (parse_operands (str, OPERANDS1(CPSF)))
+ return;
+
+ inst.instruction |= inst.operands[0].imm;
}
/* THUMB CPY instruction (argument parse). */
@@ -7225,7 +7238,10 @@
static void
do_t_setend (char * str)
{
- if (do_endian_specifier (str))
+ if (parse_operands (str, OPERANDS1(ENDI)))
+ return;
+
+ if (inst.operands[0].imm)
inst.instruction |= 0x8;
}