This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Thumb32 assembler (30/69)
- From: Zack Weinberg <zack at codesourcery dot com>
- To: binutils <binutils at sourceware dot org>
- Date: Tue, 26 Apr 2005 02:54:48 -0700
- Subject: Thumb32 assembler (30/69)
Teach parse_operands about register-or-expression operands, as appear
in the Thumb shift instructions and a few other places. OP_RR_EX
isn't used for anything yet, but will be soon. Also, fix bugs in the
handling of shifts by 0 and 32 in both md_apply_fix3 and the
disassembler.
zw
gas:
* config/tc-arm.c (struct arm_it): Add isreg field to operands.
(THUMB_ASR, THUMB_ASL, THUMB_LSR): Delete.
(BAD_HIREG): New canned diagnostic.
(OP_RR_EX, OP_RL_iEX, OP_oRL_iEX): New operand parse codes.
(parse_operands): Implement the latter two. Use BAD_HIREG throughout.
(po_reg_or_fail): Set isreg field as well.
(thumb_shift): Rename do_t_shift. Remove second argument.
Use parse_operands. Leave patching of immediate value to md_apply_fix3.
(do_t_asr, do_t_lsl, do_t_lsr): Delete.
(tinsns): Use do_t_shift for asr, lsl, lsr.
(md_apply_fix3 <case BFD_RELOC_ARM_THUMB_SHIFT>): Handle 0 and 32
correctly.
opcodes:
* arm-dis.c (print_insn_thumb): New %s escape sequence.
(thumb_opcodes): Use %s for asr and lsr.
===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c (revision 31)
+++ gas/config/tc-arm.c (revision 32)
@@ -196,6 +196,7 @@
int reg;
int imm;
int present : 1; /* operand present */
+ int isreg : 1; /* operand was a register */
int writeback : 1; /* operand has trailing ! */
} operands[6];
};
@@ -552,10 +553,6 @@
#define THUMB_H1 0x0080
#define THUMB_H2 0x0040
-#define THUMB_ASR 0
-#define THUMB_LSL 1
-#define THUMB_LSR 2
-
#define THUMB_MOVE 0
#define THUMB_COMPARE 1
#define THUMB_CPY 2
@@ -591,6 +588,7 @@
#define BAD_PC _("r15 not allowed here")
#define BAD_COND _("instruction is not conditional")
#define BAD_OVERLAP _("registers may not be the same")
+#define BAD_HIREG _("lo register required")
static struct hash_control *arm_ops_hsh;
static struct hash_control *arm_tops_hsh;
@@ -4351,6 +4349,10 @@
#define OP_CPSF 060 /* CPS flags */
#define OP_ENDI 061 /* Endianness specifier */
+/* This-or-that operands. All have bit 7 set. */
+#define OP_RR_EX 100 /* ARM register or expression */
+#define OP_RL_iEX 101 /* Thumb low register or expression with imm prefix */
+
/* Optional operands. All have the high bit set. */
#define OP_obI7 200 /* optional, prefix optional, immediate 0 .. 7 */
#define OP_obI31 201 /* 0 .. 31 */
@@ -4361,6 +4363,8 @@
#define OP_oROR 210 /* optional rotate right 0/8/16/24 */
#define OP_oRL 211 /* optional Thumb low register */
+#define OP_oRL_iEX 300 /* optional Thumb low reg or expression */
+
/* Macro for referring to one of the above constants as a number.
Should appear solely in parse_operands(). */
#define OP_(x) OP__(OP_##x)
@@ -4405,6 +4409,7 @@
return FAIL; \
} \
inst.operands[i].reg = reg_; \
+ inst.operands[i].isreg = 1; \
} while (0)
#define po_imm_or_fail(min, max, popt) do { \
@@ -4475,21 +4480,21 @@
case OP_(RL):
po_reg_or_fail (REG_TYPE_RN);
if (inst.operands[i].reg > 7)
- inst.error = _("lo register required");
+ inst.error = BAD_HIREG;
break;
case OP_(RLlb):
po_char_or_fail ('[');
po_reg_or_fail (REG_TYPE_RN);
if (inst.operands[i].reg > 7)
- inst.error = _("lo register required");
+ inst.error = BAD_HIREG;
break;
case OP_(RLtb):
po_reg_or_fail (REG_TYPE_RN);
po_char_or_fail (']');
if (inst.operands[i].reg > 7)
- inst.error = _("lo register required");
+ inst.error = BAD_HIREG;
break;
/* Immediates */
@@ -4548,6 +4553,26 @@
return FAIL;
break;
+ /* Register or expression */
+ case OP_(RR_EX):
+ abort ();
+
+ case OP_(oRL_iEX):
+ case OP_(RL_iEX):
+ if (is_immediate_prefix (*str))
+ {
+ str++;
+ if (my_get_expression (&inst.reloc.exp, &str))
+ return FAIL;
+ }
+ else
+ {
+ po_reg_or_fail (REG_TYPE_RN);
+ if (inst.operands[i].reg > 7)
+ inst.error = BAD_HIREG;
+ }
+ break;
+
/* Misc */
case OP_(CPSF):
if (parse_cps_flags (&inst.operands[i].imm, &str))
@@ -6748,106 +6773,53 @@
}
static void
-thumb_shift (char * str, int shift)
+do_t_shift (char * str)
{
- int Rd, Rs, Rn = FAIL;
+ int Rs;
+ if (parse_operands (str, OPERANDS3(RL,RL_iEX,oRL_iEX)))
+ return;
- if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
- || skip_past_comma (&str) == FAIL)
+ if (inst.operands[2].present)
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (is_immediate_prefix (*str))
- {
- /* Two operand immediate format, set Rs to Rd. */
- Rs = Rd;
- str ++;
- expression_or_fail (&inst.reloc.exp, &str);
- }
- else
- {
- if ((Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
- return;
-
- if (skip_past_comma (&str) == FAIL)
- {
- /* Two operand format, shuffle the registers
- and pretend there are 3. */
- Rn = Rs;
- Rs = Rd;
- }
- else if (is_immediate_prefix (*str))
- {
- str++;
- expression_or_fail (&inst.reloc.exp, &str);
- }
- else if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
- return;
- }
-
- /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
- for the latter case, EXPR contains the immediate that was found. */
-
- if (Rn != FAIL)
- {
- if (Rs != Rd)
- {
- inst.error = _("source1 and dest must be same register");
+ /* If there were three operands, operand 1 must be a register. */
+ if (!inst.operands[1].isreg)
+ {
+ inst.error = BAD_ARGS;
return;
}
-
- switch (shift)
+ else if (inst.operands[2].isreg) /* Rd, Rs, Rn */
{
- case THUMB_ASR: inst.instruction = T_OPCODE_ASR_R; break;
- case THUMB_LSL: inst.instruction = T_OPCODE_LSL_R; break;
- case THUMB_LSR: inst.instruction = T_OPCODE_LSR_R; break;
- }
-
- inst.instruction |= Rd | (Rn << 3);
- }
- else
- {
- switch (shift)
- {
- case THUMB_ASR: inst.instruction = T_OPCODE_ASR_I; break;
- case THUMB_LSL: inst.instruction = T_OPCODE_LSL_I; break;
- case THUMB_LSR: inst.instruction = T_OPCODE_LSR_I; break;
- }
-
- if (inst.reloc.exp.X_op != O_constant)
- {
- /* Value isn't known yet, create a dummy reloc and let reloc
- hacking fix it up. */
- inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
- }
- else
- {
- unsigned shift_value = inst.reloc.exp.X_add_number;
-
- if (shift_value > 32 || (shift_value == 32 && shift == THUMB_LSL))
+ if (inst.operands[0].reg != inst.operands[1].reg)
{
- inst.error = _("invalid immediate for shift");
+ inst.error = _("source1 and dest must be same register");
return;
}
-
- /* Shifts of zero are handled by converting to LSL. */
- if (shift_value == 0)
- inst.instruction = T_OPCODE_LSL_I;
-
- /* Shifts of 32 are encoded as a shift of zero. */
- if (shift_value == 32)
- shift_value = 0;
-
- inst.instruction |= shift_value << 6;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[2].reg << 3;
+ return;
}
-
- inst.instruction |= Rd | (Rs << 3);
+ else /* Rd, Rs, imm */
+ Rs = inst.operands[1].reg;
}
+ else if (inst.operands[1].isreg) /* Rd, Rs -> Rd, Rd, Rs */
+ {
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[2].reg << 3;
+ return;
+ }
+ else /* Rd, imm -> Rd, Rd, imm */
+ Rs = inst.operands[0].reg;
- end_of_line (str);
+ /* If we get here, we are doing a shift by an immediate. Rd and Rs
+ are the registers, and inst.reloc is the immediate. */
+ switch (inst.instruction)
+ {
+ case T_OPCODE_ASR_R: inst.instruction = T_OPCODE_ASR_I; break;
+ case T_OPCODE_LSL_R: inst.instruction = T_OPCODE_LSL_I; break;
+ case T_OPCODE_LSR_R: inst.instruction = T_OPCODE_LSR_I; break;
+ }
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ inst.instruction |= inst.operands[0].reg | (Rs << 3);
}
static void
@@ -6907,12 +6879,6 @@
inst.instruction |= inst.operands[1].reg << 3;
}
-static void
-do_t_asr (char * str)
-{
- thumb_shift (str, THUMB_ASR);
-}
-
/* THUMB V5 breakpoint instruction (argument parse)
BKPT <immed_8>. */
@@ -7111,18 +7077,6 @@
}
static void
-do_t_lsl (char * str)
-{
- thumb_shift (str, THUMB_LSL);
-}
-
-static void
-do_t_lsr (char * str)
-{
- thumb_shift (str, THUMB_LSR);
-}
-
-static void
do_t_mov (char * str)
{
thumb_mov_compare (str, THUMB_MOVE);
@@ -9977,7 +9931,7 @@
{"adc", 0x4140, 2, ARM_EXT_V4T, do_t_arit},
{"add", 0x0000, 2, ARM_EXT_V4T, do_t_add},
{"and", 0x4000, 2, ARM_EXT_V4T, do_t_arit},
- {"asr", 0x0000, 2, ARM_EXT_V4T, do_t_asr},
+ {"asr", 0x4100, 2, ARM_EXT_V4T, do_t_shift},
{"b", T_OPCODE_BRANCH, 2, ARM_EXT_V4T, do_t_branch12},
{"beq", 0xd0fe, 2, ARM_EXT_V4T, do_t_branch9},
{"bne", 0xd1fe, 2, ARM_EXT_V4T, do_t_branch9},
@@ -10011,8 +9965,8 @@
{"ldrsh", 0x5e00, 2, ARM_EXT_V4T, do_t_lds},
{"ldsb", 0x5600, 2, ARM_EXT_V4T, do_t_lds},
{"ldsh", 0x5e00, 2, ARM_EXT_V4T, do_t_lds},
- {"lsl", 0x0000, 2, ARM_EXT_V4T, do_t_lsl},
- {"lsr", 0x0000, 2, ARM_EXT_V4T, do_t_lsr},
+ {"lsl", 0x4080, 2, ARM_EXT_V4T, do_t_shift},
+ {"lsr", 0x40c0, 2, ARM_EXT_V4T, do_t_shift},
{"mov", 0x0000, 2, ARM_EXT_V4T, do_t_mov},
{"mul", T_OPCODE_MUL, 2, ARM_EXT_V4T, do_t_arit},
{"mvn", T_OPCODE_MVN, 2, ARM_EXT_V4T, do_t_arit},
@@ -11412,11 +11366,18 @@
break;
case BFD_RELOC_ARM_THUMB_SHIFT:
- /* 5bit shift value (0..31). */
- if (value < 0 || value > 31)
+ /* 5bit shift value (0..32). LSL cannot take 32. */
+ newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf83f;
+ temp = newval & 0xf800;
+ if (value < 0 || value > 32 || (value == 32 && temp == T_OPCODE_LSL_I))
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("illegal Thumb shift value: %ld"), (long) value);
- newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf03f;
+ _("invalid shift value: %ld"), (long) value);
+ /* Shifts of zero must be encoded as LSL. */
+ if (value == 0)
+ newval = (newval & 0x003f) | T_OPCODE_LSL_I;
+ /* Shifts of 32 are encoded as zero. */
+ else if (value == 32)
+ value = 0;
newval |= value << 6;
md_number_to_chars (buf, newval, THUMB_SIZE);
break;
===================================================================
Index: opcodes/arm-dis.c
--- opcodes/arm-dis.c (revision 31)
+++ opcodes/arm-dis.c (revision 32)
@@ -104,7 +104,8 @@
%<bitfield>W print (bitfield * 4) as a decimal
%<bitfield>H print (bitfield * 2) as a decimal
%<bitfield>a print (bitfield * 4) as a pc-rel offset + decoded symbol
- %e print arm SMI operand (bits 0..7,8..19). */
+ %e print arm SMI operand (bits 0..7,8..19).
+ %s print Thumb right-shift immediate (6..10; 0 == 32). */
/* Note: There is a partial ordering in this table - it must be searched from
the top to obtain a correct match. */
@@ -710,8 +711,8 @@
{ARM_EXT_V4T, 0x5800, 0xFA00, "ldr%10'b\t%0-2r, [%3-5r, %6-8r]"},
/* format 1 */
{ARM_EXT_V4T, 0x0000, 0xF800, "lsl\t%0-2r, %3-5r, #%6-10d"},
- {ARM_EXT_V4T, 0x0800, 0xF800, "lsr\t%0-2r, %3-5r, #%6-10d"},
- {ARM_EXT_V4T, 0x1000, 0xF800, "asr\t%0-2r, %3-5r, #%6-10d"},
+ {ARM_EXT_V4T, 0x0800, 0xF800, "lsr\t%0-2r, %3-5r, %s"},
+ {ARM_EXT_V4T, 0x1000, 0xF800, "asr\t%0-2r, %3-5r, %s"},
/* format 3 */
{ARM_EXT_V4T, 0x2000, 0xF800, "mov\t%8-10r, #%0-7d"},
{ARM_EXT_V4T, 0x2800, 0xF800, "cmp\t%8-10r, #%0-7d"},
@@ -1814,6 +1815,16 @@
}
break;
+ case 's':
+ /* Right shift immediate -- bits 6..10; 1-31 print
+ as themselves, 0 prints as 32. */
+ {
+ long imm = (given & 0x07c0) >> 6;
+ if (imm == 0)
+ imm = 32;
+ func (stream, "#%d", imm);
+ }
+ break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':