#define ARM_EXT_V5ExP 0x00000200 /* DSP core set. */
#define ARM_EXT_V5E 0x00000400 /* DSP Double transfers. */
#define ARM_EXT_V5J 0x00000800 /* Jazelle extension. */
+#define ARM_EXT_V6 0x00001000 /* ARM V6. */
/* Co-processor space extensions. */
#define ARM_CEXT_XSCALE 0x00800000 /* Allow MIA etc. */
#define ARM_ARCH_V5TExP (ARM_ARCH_V5T | ARM_EXT_V5ExP)
#define ARM_ARCH_V5TE (ARM_ARCH_V5TExP | ARM_EXT_V5E)
#define ARM_ARCH_V5TEJ (ARM_ARCH_V5TE | ARM_EXT_V5J)
+#define ARM_ARCH_V6 (ARM_ARCH_V5TEJ | ARM_EXT_V6)
/* Processors with specific extensions in the co-processor space. */
#define ARM_ARCH_XSCALE (ARM_ARCH_V5TE | ARM_CEXT_XSCALE)
#define FPU_VFP_EXT_V1xD 0x10000000 /* Base VFP instruction set. */
#define FPU_VFP_EXT_V1 0x08000000 /* Double-precision insns. */
#define FPU_VFP_EXT_V2 0x04000000 /* ARM10E VFPr1. */
+#define FPU_MAVERICK 0x02000000 /* Cirrus Maverick. */
#define FPU_NONE 0
#define FPU_ARCH_FPE FPU_FPA_EXT_V1
#define FPU_ARCH_VFP_V1 (FPU_ARCH_VFP_V1xD | FPU_VFP_EXT_V1)
#define FPU_ARCH_VFP_V2 (FPU_ARCH_VFP_V1 | FPU_VFP_EXT_V2)
+#define FPU_ARCH_MAVERICK FPU_MAVERICK
+
+enum arm_float_abi
+{
+ ARM_FLOAT_ABI_HARD,
+ ARM_FLOAT_ABI_SOFTFP,
+ ARM_FLOAT_ABI_SOFT
+};
+
/* Types of processor to assemble for. */
#define ARM_1 ARM_ARCH_V1
#define ARM_2 ARM_ARCH_V2
static int march_cpu_opt = -1;
static int march_fpu_opt = -1;
static int mfpu_opt = -1;
+static int mfloat_abi_opt = -1;
/* This array holds the chars that always start a comment. If the
pre-processor is disabled, these aren't very useful. */
{ "RRX", shift_properties + SHIFT_RRX }
};
+/* Any kind of shift is accepted. */
#define NO_SHIFT_RESTRICT 1
-#define SHIFT_RESTRICT 0
+/* The shift operand must be an immediate value, not a register. */
+#define SHIFT_IMMEDIATE 0
+/* The shift must be LSL or ASR and the operand must be an immediate. */
+#define SHIFT_LSL_OR_ASR_IMMEDIATE 2
+/* The shift must be ASR and the operand must be an immediate. */
+#define SHIFT_ASR_IMMEDIATE 3
+/* The shift must be LSL and the operand must be an immediate. */
+#define SHIFT_LSL_IMMEDIATE 4
#define NUM_FLOAT_VALS 8
/* ARM v5TEJ. */
static void do_bxj PARAMS ((char *));
+/* ARM V6. */
+static void do_cps PARAMS ((char *));
+static void do_cpsi PARAMS ((char *));
+static void do_ldrex PARAMS ((char *));
+static void do_pkhbt PARAMS ((char *));
+static void do_pkhtb PARAMS ((char *));
+static void do_qadd16 PARAMS ((char *));
+static void do_rev PARAMS ((char *));
+static void do_rfe PARAMS ((char *));
+static void do_sxtah PARAMS ((char *));
+static void do_sxth PARAMS ((char *));
+static void do_setend PARAMS ((char *));
+static void do_smlad PARAMS ((char *));
+static void do_smlald PARAMS ((char *));
+static void do_smmul PARAMS ((char *));
+static void do_ssat PARAMS ((char *));
+static void do_usat PARAMS ((char *));
+static void do_srs PARAMS ((char *));
+static void do_ssat16 PARAMS ((char *));
+static void do_usat16 PARAMS ((char *));
+static void do_strex PARAMS ((char *));
+static void do_umaal PARAMS ((char *));
+
+static void do_cps_mode PARAMS ((char **));
+static void do_cps_flags PARAMS ((char **, int));
+static int do_endian_specifier PARAMS ((char *));
+static void do_pkh_core PARAMS ((char *, int));
+static void do_sat PARAMS ((char **, int));
+static void do_sat16 PARAMS ((char **, int));
+
/* Coprocessor Instructions. */
static void do_cdp PARAMS ((char *));
static void do_lstc PARAMS ((char *));
static void do_vfp_dp_dyadic PARAMS ((char *));
static void do_vfp_reg_from_sp PARAMS ((char *));
static void do_vfp_sp_from_reg PARAMS ((char *));
-static void do_vfp_sp_reg2 PARAMS ((char *));
+static void do_vfp_reg2_from_sp2 PARAMS ((char *));
+static void do_vfp_sp2_from_reg2 PARAMS ((char *));
static void do_vfp_reg_from_dp PARAMS ((char *));
static void do_vfp_reg2_from_dp PARAMS ((char *));
static void do_vfp_dp_from_reg PARAMS ((char *));
{"mla", 0xe0200090, 3, ARM_EXT_V2, do_mla},
{"mlas", 0xe0300090, 3, ARM_EXT_V2, do_mla},
- /* Generic copressor instructions. */
+ /* Generic coprocessor instructions. */
{"cdp", 0xee000000, 3, ARM_EXT_V2, do_cdp},
{"ldc", 0xec100000, 3, ARM_EXT_V2, do_lstc},
{"ldcl", 0xec500000, 3, ARM_EXT_V2, do_lstc},
/* ARM Architecture 5TEJ. */
{"bxj", 0xe12fff20, 3, ARM_EXT_V5J, do_bxj},
+ /* ARM V6. */
+ { "cps", 0xf1020000, 0, ARM_EXT_V6, do_cps},
+ { "cpsie", 0xf1080000, 0, ARM_EXT_V6, do_cpsi},
+ { "cpsid", 0xf10C0000, 0, ARM_EXT_V6, do_cpsi},
+ { "ldrex", 0xe1900f9f, 5, ARM_EXT_V6, do_ldrex},
+ { "mcrr2", 0xfc400000, 0, ARM_EXT_V6, do_co_reg2c},
+ { "mrrc2", 0xfc500000, 0, ARM_EXT_V6, do_co_reg2c},
+ { "pkhbt", 0xe6800010, 5, ARM_EXT_V6, do_pkhbt},
+ { "pkhtb", 0xe6800050, 5, ARM_EXT_V6, do_pkhtb},
+ { "qadd16", 0xe6200f10, 6, ARM_EXT_V6, do_qadd16},
+ { "qadd8", 0xe6200f90, 5, ARM_EXT_V6, do_qadd16},
+ { "qaddsubx", 0xe6200f30, 8, ARM_EXT_V6, do_qadd16},
+ { "qsub16", 0xe6200f70, 6, ARM_EXT_V6, do_qadd16},
+ { "qsub8", 0xe6200ff0, 5, ARM_EXT_V6, do_qadd16},
+ { "qsubaddx", 0xe6200f50, 8, ARM_EXT_V6, do_qadd16},
+ { "sadd16", 0xe6100f10, 6, ARM_EXT_V6, do_qadd16},
+ { "sadd8", 0xe6100f90, 5, ARM_EXT_V6, do_qadd16},
+ { "saddsubx", 0xe6100f30, 8, ARM_EXT_V6, do_qadd16},
+ { "shadd16", 0xe6300f10, 7, ARM_EXT_V6, do_qadd16},
+ { "shadd8", 0xe6300f90, 6, ARM_EXT_V6, do_qadd16},
+ { "shaddsubx", 0xe6300f30, 9, ARM_EXT_V6, do_qadd16},
+ { "shsub16", 0xe6300f70, 7, ARM_EXT_V6, do_qadd16},
+ { "shsub8", 0xe6300ff0, 6, ARM_EXT_V6, do_qadd16},
+ { "shsubaddx", 0xe6300f50, 9, ARM_EXT_V6, do_qadd16},
+ { "ssub16", 0xe6100f70, 6, ARM_EXT_V6, do_qadd16},
+ { "ssub8", 0xe6100ff0, 5, ARM_EXT_V6, do_qadd16},
+ { "ssubaddx", 0xe6100f50, 8, ARM_EXT_V6, do_qadd16},
+ { "uadd16", 0xe6500f10, 6, ARM_EXT_V6, do_qadd16},
+ { "uadd8", 0xe6500f90, 5, ARM_EXT_V6, do_qadd16},
+ { "uaddsubx", 0xe6500f30, 8, ARM_EXT_V6, do_qadd16},
+ { "uhadd16", 0xe6700f10, 7, ARM_EXT_V6, do_qadd16},
+ { "uhadd8", 0xe6700f90, 6, ARM_EXT_V6, do_qadd16},
+ { "uhaddsubx", 0xe6700f30, 9, ARM_EXT_V6, do_qadd16},
+ { "uhsub16", 0xe6700f70, 7, ARM_EXT_V6, do_qadd16},
+ { "uhsub8", 0xe6700ff0, 6, ARM_EXT_V6, do_qadd16},
+ { "uhsubaddx", 0xe6700f50, 9, ARM_EXT_V6, do_qadd16},
+ { "uqadd16", 0xe6600f10, 7, ARM_EXT_V6, do_qadd16},
+ { "uqadd8", 0xe6600f90, 6, ARM_EXT_V6, do_qadd16},
+ { "uqaddsubx", 0xe6600f30, 9, ARM_EXT_V6, do_qadd16},
+ { "uqsub16", 0xe6600f70, 7, ARM_EXT_V6, do_qadd16},
+ { "uqsub8", 0xe6600ff0, 6, ARM_EXT_V6, do_qadd16},
+ { "uqsubaddx", 0xe6600f50, 9, ARM_EXT_V6, do_qadd16},
+ { "usub16", 0xe6500f70, 6, ARM_EXT_V6, do_qadd16},
+ { "usub8", 0xe6500ff0, 5, ARM_EXT_V6, do_qadd16},
+ { "usubaddx", 0xe6500f50, 8, ARM_EXT_V6, do_qadd16},
+ { "rev", 0xe6bf0f30, 3, ARM_EXT_V6, do_rev},
+ { "rev16", 0xe6bf0fb0, 5, ARM_EXT_V6, do_rev},
+ { "revsh", 0xe6ff0fb0, 5, ARM_EXT_V6, do_rev},
+ { "rfeia", 0xf8900a00, 0, ARM_EXT_V6, do_rfe},
+ { "rfeib", 0xf9900a00, 0, ARM_EXT_V6, do_rfe},
+ { "rfeda", 0xf8100a00, 0, ARM_EXT_V6, do_rfe},
+ { "rfedb", 0xf9100a00, 0, ARM_EXT_V6, do_rfe},
+ { "rfefd", 0xf8900a00, 0, ARM_EXT_V6, do_rfe},
+ { "rfefa", 0xf9900a00, 0, ARM_EXT_V6, do_rfe},
+ { "rfeea", 0xf8100a00, 0, ARM_EXT_V6, do_rfe},
+ { "rfeed", 0xf9100a00, 0, ARM_EXT_V6, do_rfe},
+ { "sxtah", 0xe6b00070, 5, ARM_EXT_V6, do_sxtah},
+ { "sxtab16", 0xe6800070, 7, ARM_EXT_V6, do_sxtah},
+ { "sxtab", 0xe6a00070, 5, ARM_EXT_V6, do_sxtah},
+ { "sxth", 0xe6bf0070, 4, ARM_EXT_V6, do_sxth},
+ { "sxtb16", 0xe68f0070, 6, ARM_EXT_V6, do_sxth},
+ { "sxtb", 0xe6af0070, 4, ARM_EXT_V6, do_sxth},
+ { "uxtah", 0xe6f00070, 5, ARM_EXT_V6, do_sxtah},
+ { "uxtab16", 0xe6c00070, 7, ARM_EXT_V6, do_sxtah},
+ { "uxtab", 0xe6e00070, 5, ARM_EXT_V6, do_sxtah},
+ { "uxth", 0xe6ff0070, 4, ARM_EXT_V6, do_sxth},
+ { "uxtb16", 0xe6cf0070, 6, ARM_EXT_V6, do_sxth},
+ { "uxtb", 0xe6ef0070, 4, ARM_EXT_V6, do_sxth},
+ { "sel", 0xe68000b0, 3, ARM_EXT_V6, do_qadd16},
+ { "setend", 0xf1010000, 0, ARM_EXT_V6, do_setend},
+ { "smlad", 0xe7000010, 5, ARM_EXT_V6, do_smlad},
+ { "smladx", 0xe7000030, 6, ARM_EXT_V6, do_smlad},
+ { "smlald", 0xe7400010, 6, ARM_EXT_V6, do_smlald},
+ { "smlaldx", 0xe7400030, 7, ARM_EXT_V6, do_smlald},
+ { "smlsd", 0xe7000050, 5, ARM_EXT_V6, do_smlad},
+ { "smlsdx", 0xe7000070, 6, ARM_EXT_V6, do_smlad},
+ { "smlsld", 0xe7400050, 6, ARM_EXT_V6, do_smlald},
+ { "smlsldx", 0xe7400070, 7, ARM_EXT_V6, do_smlald},
+ { "smmla", 0xe7500010, 5, ARM_EXT_V6, do_smlad},
+ { "smmlar", 0xe7500030, 6, ARM_EXT_V6, do_smlad},
+ { "smmls", 0xe75000d0, 5, ARM_EXT_V6, do_smlad},
+ { "smmlsr", 0xe75000f0, 6, ARM_EXT_V6, do_smlad},
+ { "smmul", 0xe750f010, 5, ARM_EXT_V6, do_smmul},
+ { "smmulr", 0xe750f030, 6, ARM_EXT_V6, do_smmul},
+ { "smuad", 0xe700f010, 5, ARM_EXT_V6, do_smmul},
+ { "smuadx", 0xe700f030, 6, ARM_EXT_V6, do_smmul},
+ { "smusd", 0xe700f050, 5, ARM_EXT_V6, do_smmul},
+ { "smusdx", 0xe700f070, 6, ARM_EXT_V6, do_smmul},
+ { "srsia", 0xf8cd0500, 0, ARM_EXT_V6, do_srs},
+ { "srsib", 0xf9cd0500, 0, ARM_EXT_V6, do_srs},
+ { "srsda", 0xf84d0500, 0, ARM_EXT_V6, do_srs},
+ { "srsdb", 0xf94d0500, 0, ARM_EXT_V6, do_srs},
+ { "ssat", 0xe6a00010, 4, ARM_EXT_V6, do_ssat},
+ { "ssat16", 0xe6a00f30, 6, ARM_EXT_V6, do_ssat16},
+ { "strex", 0xe1800f90, 5, ARM_EXT_V6, do_strex},
+ { "umaal", 0xe0400090, 5, ARM_EXT_V6, do_umaal},
+ { "usad8", 0xe780f010, 5, ARM_EXT_V6, do_smmul},
+ { "usada8", 0xe7800010, 6, ARM_EXT_V6, do_smlad},
+ { "usat", 0xe6e00010, 4, ARM_EXT_V6, do_usat},
+ { "usat16", 0xe6e00f30, 6, ARM_EXT_V6, do_usat16},
+
/* Core FPA instruction set (V1). */
{"wfs", 0xee200110, 3, FPU_FPA_EXT_V1, do_fpa_ctrl},
{"rfs", 0xee300110, 3, FPU_FPA_EXT_V1, do_fpa_ctrl},
{"fcmpezd", 0xeeb50bc0, 7, FPU_VFP_EXT_V1, do_vfp_dp_compare_z},
/* VFP V2. */
- {"fmsrr", 0xec400a10, 5, FPU_VFP_EXT_V2, do_vfp_sp_reg2},
- {"fmrrs", 0xec500a10, 5, FPU_VFP_EXT_V2, do_vfp_sp_reg2},
+ {"fmsrr", 0xec400a10, 5, FPU_VFP_EXT_V2, do_vfp_sp2_from_reg2},
+ {"fmrrs", 0xec500a10, 5, FPU_VFP_EXT_V2, do_vfp_reg2_from_sp2},
{"fmdrr", 0xec400b10, 5, FPU_VFP_EXT_V2, do_vfp_dp_from_reg2},
{"fmrrd", 0xec500b10, 5, FPU_VFP_EXT_V2, do_vfp_reg2_from_dp},
static void do_t_blx PARAMS ((char *));
static void do_t_bkpt PARAMS ((char *));
+/* ARM V6. */
+static void do_t_cps PARAMS ((char *));
+static void do_t_cpy PARAMS ((char *));
+static void do_t_setend PARAMS ((char *));;
+
#define T_OPCODE_MUL 0x4340
#define T_OPCODE_TST 0x4200
#define T_OPCODE_CMN 0x42c0
#define THUMB_MOVE 0
#define THUMB_COMPARE 1
+#define THUMB_CPY 2
#define THUMB_LOAD 0
#define THUMB_STORE 1
/* Thumb v2 (ARMv5T). */
{"blx", 0, 0, ARM_EXT_V5T, do_t_blx},
{"bkpt", 0xbe00, 2, ARM_EXT_V5T, do_t_bkpt},
+
+ /* ARM V6. */
+ {"cpsie", 0xb660, 2, ARM_EXT_V6, do_t_cps},
+ {"cpsid", 0xb670, 2, ARM_EXT_V6, do_t_cps},
+ {"cpy", 0x4600, 2, ARM_EXT_V6, do_t_cpy},
+ {"rev", 0xba00, 2, ARM_EXT_V6, do_t_arit},
+ {"rev16", 0xba40, 2, ARM_EXT_V6, do_t_arit},
+ {"revsh", 0xbac0, 2, ARM_EXT_V6, do_t_arit},
+ {"setend", 0xb650, 2, ARM_EXT_V6, do_t_setend},
+ {"sxth", 0xb200, 2, ARM_EXT_V6, do_t_arit},
+ {"sxtb", 0xb240, 2, ARM_EXT_V6, do_t_arit},
+ {"uxth", 0xb280, 2, ARM_EXT_V6, do_t_arit},
+ {"uxtb", 0xb2c0, 2, ARM_EXT_V6, do_t_arit},
};
#define BAD_ARGS _("bad arguments to instruction")
static int arm_parse_cpu PARAMS ((char *));
static int arm_parse_arch PARAMS ((char *));
static int arm_parse_fpu PARAMS ((char *));
+static int arm_parse_float_abi PARAMS ((char *));
#if 0 /* Suppressed - for now. */
#if defined OBJ_COFF || defined OBJ_ELF
static void arm_add_note PARAMS ((const char *, const char *, unsigned int));
if (!req_entry)
as_bad (_("unreq: missing hash entry for \"%s\""), name);
else if (req_entry->builtin)
- /* FIXME: We are deleteing a built in register alias which
+ /* FIXME: We are deleting a built in register alias which
points to a const data structure, so we only need to
free up the memory used by the key in the hash table.
Unfortunately we have not recorded this value, so this
;
else
{
- /* Deleteing a user defined alias. We need to free the
+ /* Deleting a user defined alias. We need to free the
key and the value, but fortunately the key is the same
as the value->name field. */
free ((char *) req_entry->name);
{
/* Do nothing really. */
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
/* Expects *str -> the characters "acc0", possibly with leading blanks.
return;
}
}
-
+
+ end_of_line (str);
+}
+
+/* ARM V5 (argument parse)
+ MCR2 <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
+ MRC2 <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
+ Instruction is not conditional, and has 0xf in the condition field.
+ Otherwise, it's the same as MCR/MRC. */
+
+static void
+do_co_reg2 (str)
+ char * str;
+{
+ skip_whitespace (str);
+
+ if (co_proc_number (& str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_opc_expr (& str, 21, 3) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || reg_required_here (& str, 12) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_reg_required_here (& str, 16) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_reg_required_here (& str, 0) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == SUCCESS)
+ {
+ if (cp_opc_expr (& str, 5, 3) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+ }
+
+ end_of_line (str);
+}
+
+/* ARM v5TEJ. Jump to Jazelle code. */
+static void
+do_bxj (str)
+ char * str;
+{
+ int reg;
+
+ skip_whitespace (str);
+
+ if ((reg = reg_required_here (&str, 0)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ /* Note - it is not illegal to do a "bxj pc". Useless, but not illegal. */
+ if (reg == REG_PC)
+ as_tsktsk (_("use of r15 in bxj is not really useful"));
+
+ end_of_line (str);
+}
+
+/* ARM V6 umaal (argument parse). */
+
+static void
+do_umaal (str)
+ char *str;
+{
+
+ int rdlo, rdhi, rm, rs;
+
+ skip_whitespace (str);
+ if ((rdlo = reg_required_here (& str, 12)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rdhi = reg_required_here (& str, 16)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rm = reg_required_here (& str, 0)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rs = reg_required_here (& str, 8)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rdlo == REG_PC || rdhi == REG_PC || rm == REG_PC || rs == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ end_of_line (str);
+}
+
+/* ARM V6 strex (argument parse). */
+
+static void
+do_strex (str)
+ char *str;
+{
+ int rd, rm, rn;
+
+ /* Parse Rd, Rm,. */
+ skip_whitespace (str);
+ if ((rd = reg_required_here (& str, 12)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rm = reg_required_here (& str, 0)) == FAIL
+ || skip_past_comma (& str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ if (rd == REG_PC || rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+ if (rd == rm)
+ {
+ inst.error = _("Rd equal to Rm or Rn yields unpredictable results");
+ return;
+ }
+
+ /* Skip past '['. */
+ if ((strlen (str) >= 1)
+ && strncmp (str, "[", 1) == 0)
+ str+=1;
+ skip_whitespace (str);
+
+ /* Parse Rn. */
+ if ((rn = reg_required_here (& str, 16)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ else if (rn == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+ if (rd == rn)
+ {
+ inst.error = _("Rd equal to Rm or Rn yields unpredictable results");
+ return;
+ }
+ skip_whitespace (str);
+
+ /* Skip past ']'. */
+ if ((strlen (str) >= 1)
+ && strncmp (str, "]", 1) == 0)
+ str+=1;
+
+ end_of_line (str);
+}
+
+/* ARM V6 ssat (argument parse). */
+
+static void
+do_ssat (str)
+ char* str;
+{
+ do_sat (&str, /*bias=*/-1);
+ end_of_line (str);
+}
+
+/* ARM V6 usat (argument parse). */
+
+static void
+do_usat (str)
+ char* str;
+{
+ do_sat (&str, /*bias=*/0);
+ end_of_line (str);
+}
+
+static void
+do_sat (str, bias)
+ char **str;
+ int bias;
+{
+ int rd, rm;
+ expressionS expr;
+
+ skip_whitespace (*str);
+
+ /* Parse <Rd>, field. */
+ if ((rd = reg_required_here (str, 12)) == FAIL
+ || skip_past_comma (str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ if (rd == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ /* Parse #<immed>, field. */
+ if (is_immediate_prefix (**str))
+ (*str)++;
+ else
+ {
+ inst.error = _("immediate expression expected");
+ return;
+ }
+ if (my_get_expression (&expr, str))
+ {
+ inst.error = _("bad expression");
+ return;
+ }
+ if (expr.X_op != O_constant)
+ {
+ inst.error = _("constant expression expected");
+ return;
+ }
+ if (expr.X_add_number + bias < 0
+ || expr.X_add_number + bias > 31)
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+ inst.instruction |= (expr.X_add_number + bias) << 16;
+ if (skip_past_comma (str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ /* Parse <Rm> field. */
+ if ((rm = reg_required_here (str, 0)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ if (rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ if (skip_past_comma (str) == SUCCESS)
+ decode_shift (str, SHIFT_LSL_OR_ASR_IMMEDIATE);
+}
+
+/* ARM V6 ssat16 (argument parse). */
+
+static void
+do_ssat16 (str)
+ char *str;
+{
+ do_sat16 (&str, /*bias=*/-1);
+ end_of_line (str);
+}
+
+static void
+do_usat16 (str)
+ char *str;
+{
+ do_sat16 (&str, /*bias=*/0);
+ end_of_line (str);
+}
+
+static void
+do_sat16 (str, bias)
+ char **str;
+ int bias;
+{
+ int rd, rm;
+ expressionS expr;
+
+ skip_whitespace (*str);
+
+ /* Parse the <Rd> field. */
+ if ((rd = reg_required_here (str, 12)) == FAIL
+ || skip_past_comma (str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ if (rd == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ /* Parse #<immed>, field. */
+ if (is_immediate_prefix (**str))
+ (*str)++;
+ else
+ {
+ inst.error = _("immediate expression expected");
+ return;
+ }
+ if (my_get_expression (&expr, str))
+ {
+ inst.error = _("bad expression");
+ return;
+ }
+ if (expr.X_op != O_constant)
+ {
+ inst.error = _("constant expression expected");
+ return;
+ }
+ if (expr.X_add_number + bias < 0
+ || expr.X_add_number + bias > 15)
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+ inst.instruction |= (expr.X_add_number + bias) << 16;
+ if (skip_past_comma (str) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ /* Parse <Rm> field. */
+ if ((rm = reg_required_here (str, 0)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ if (rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+}
+
+/* ARM V6 srs (argument parse). */
+
+static void
+do_srs (str)
+ char* str;
+{
+ char *exclam;
+ skip_whitespace (str);
+ exclam = strchr (str, '!');
+ if (exclam)
+ *exclam = '\0';
+ do_cps_mode (&str);
+ if (exclam)
+ *exclam = '!';
+ if (*str == '!')
+ {
+ inst.instruction |= WRITE_BACK;
+ str++;
+ }
+ end_of_line (str);
+}
+
+/* ARM V6 SMMUL (argument parse). */
+
+static void
+do_smmul (str)
+ char* str;
+{
+ int rd, rm, rs;
+
+ skip_whitespace (str);
+ if ((rd = reg_required_here (&str, 16)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rs = reg_required_here (&str, 8)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rd == REG_PC
+ || rm == REG_PC
+ || rs == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ end_of_line (str);
+
+}
+
+/* ARM V6 SMLALD (argument parse). */
+
+static void
+do_smlald (str)
+ char* str;
+{
+ int rdlo, rdhi, rm, rs;
+ skip_whitespace (str);
+ if ((rdlo = reg_required_here (&str, 12)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rdhi = reg_required_here (&str, 16)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rs = reg_required_here (&str, 8)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rdlo == REG_PC
+ || rdhi == REG_PC
+ || rm == REG_PC
+ || rs == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ end_of_line (str);
+}
+
+/* ARM V6 SMLAD (argument parse). Signed multiply accumulate dual.
+ smlad{x}{<cond>} Rd, Rm, Rs, Rn */
+
+static void
+do_smlad (str)
+ char *str;
+{
+ int rd, rm, rs, rn;
+
+ skip_whitespace (str);
+ if ((rd = reg_required_here (&str, 16)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rs = reg_required_here (&str, 8)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rn = reg_required_here (&str, 12)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rd == REG_PC
+ || rn == REG_PC
+ || rs == REG_PC
+ || rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ end_of_line (str);
+}
+
+/* ARM V6 SETEND (argument parse). Sets the E bit in the CPSR while
+ preserving the other bits.
+
+ setend <endian_specifier>, where <endian_specifier> is either
+ BE or LE. */
+
+static void
+do_setend (str)
+ char *str;
+{
+ if (do_endian_specifier (str))
+ inst.instruction |= 0x200;
+}
+
+/* Returns true if the endian-specifier indicates big-endianness. */
+
+static int
+do_endian_specifier (str)
+ char *str;
+{
+ int big_endian = 0;
+
+ skip_whitespace (str);
+ if (strlen (str) < 2)
+ inst.error = _("missing endian specifier");
+ else if (strncasecmp (str, "BE", 2) == 0)
+ {
+ str += 2;
+ big_endian = 1;
+ }
+ else if (strncasecmp (str, "LE", 2) == 0)
+ str += 2;
+ else
+ inst.error = _("valid endian specifiers are be or le");
+
+ end_of_line (str);
+
+ return big_endian;
+}
+
+/* ARM V6 SXTH.
+
+ SXTH {<cond>} <Rd>, <Rm>{, <rotation>}
+ Condition defaults to COND_ALWAYS.
+ Error if any register uses R15. */
+
+static void
+do_sxth (str)
+ char *str;
+{
+ int rd, rm;
+ expressionS expr;
+ int rotation_clear_mask = 0xfffff3ff;
+ int rotation_eight_mask = 0x00000400;
+ int rotation_sixteen_mask = 0x00000800;
+ int rotation_twenty_four_mask = 0x00000c00;
+
+ skip_whitespace (str);
+ if ((rd = reg_required_here (&str, 12)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ else if (rd == REG_PC || rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ /* Zero out the rotation field. */
+ inst.instruction &= rotation_clear_mask;
+
+ /* Check for lack of optional rotation field. */
+ if (skip_past_comma (&str) == FAIL)
+ {
+ end_of_line (str);
+ return;
+ }
+
+ /* Move past 'ROR'. */
+ skip_whitespace (str);
+ if (strncasecmp (str, "ROR", 3) == 0)
+ str+=3;
+ else
+ {
+ inst.error = _("missing rotation field after comma");
+ return;
+ }
+
+ /* Get the immediate constant. */
+ skip_whitespace (str);
+ if (is_immediate_prefix (* str))
+ str++;
+ else
+ {
+ inst.error = _("immediate expression expected");
+ return;
+ }
+
+ if (my_get_expression (&expr, &str))
+ {
+ inst.error = _("bad expression");
+ return;
+ }
+
+ if (expr.X_op != O_constant)
+ {
+ inst.error = _("constant expression expected");
+ return;
+ }
+
+ switch (expr.X_add_number)
+ {
+ case 0:
+ /* Rotation field has already been zeroed. */
+ break;
+ case 8:
+ inst.instruction |= rotation_eight_mask;
+ break;
+
+ case 16:
+ inst.instruction |= rotation_sixteen_mask;
+ break;
+
+ case 24:
+ inst.instruction |= rotation_twenty_four_mask;
+ break;
+
+ default:
+ inst.error = _("rotation can be 8, 16, 24 or 0 when field is ommited");
+ break;
+ }
+
+ end_of_line (str);
+
+}
+
+/* ARM V6 SXTAH extracts a 16-bit value from a register, sign
+ extends it to 32-bits, and adds the result to a value in another
+ register. You can specify a rotation by 0, 8, 16, or 24 bits
+ before extracting the 16-bit value.
+ SXTAH{<cond>} <Rd>, <Rn>, <Rm>{, <rotation>}
+ Condition defaults to COND_ALWAYS.
+ Error if any register uses R15. */
+
+static void
+do_sxtah (str)
+ char *str;
+{
+ int rd, rn, rm;
+ expressionS expr;
+ int rotation_clear_mask = 0xfffff3ff;
+ int rotation_eight_mask = 0x00000400;
+ int rotation_sixteen_mask = 0x00000800;
+ int rotation_twenty_four_mask = 0x00000c00;
+
+ skip_whitespace (str);
+ if ((rd = reg_required_here (&str, 12)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rn = reg_required_here (&str, 16)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ else if (rd == REG_PC || rn == REG_PC || rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ /* Zero out the rotation field. */
+ inst.instruction &= rotation_clear_mask;
+
+ /* Check for lack of optional rotation field. */
+ if (skip_past_comma (&str) == FAIL)
+ {
+ end_of_line (str);
+ return;
+ }
+
+ /* Move past 'ROR'. */
+ skip_whitespace (str);
+ if (strncasecmp (str, "ROR", 3) == 0)
+ str+=3;
+ else
+ {
+ inst.error = _("missing rotation field after comma");
+ return;
+ }
+
+ /* Get the immediate constant. */
+ skip_whitespace (str);
+ if (is_immediate_prefix (* str))
+ str++;
+ else
+ {
+ inst.error = _("immediate expression expected");
+ return;
+ }
+
+ if (my_get_expression (&expr, &str))
+ {
+ inst.error = _("bad expression");
+ return;
+ }
+
+ if (expr.X_op != O_constant)
+ {
+ inst.error = _("constant expression expected");
+ return;
+ }
+
+ switch (expr.X_add_number)
+ {
+ case 0:
+ /* Rotation field has already been zeroed. */
+ break;
+
+ case 8:
+ inst.instruction |= rotation_eight_mask;
+ break;
+
+ case 16:
+ inst.instruction |= rotation_sixteen_mask;
+ break;
+
+ case 24:
+ inst.instruction |= rotation_twenty_four_mask;
+ break;
+
+ default:
+ inst.error = _("rotation can be 8, 16, 24 or 0 when field is ommited");
+ break;
+ }
+
+ end_of_line (str);
+
+}
+
+
+/* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the
+ word at the specified address and the following word
+ respectively.
+ Unconditionally executed.
+ Error if Rn is R15.
+*/
+
+static void
+do_rfe (str)
+ char *str;
+{
+ int rn;
+
+ skip_whitespace (str);
+
+ if ((rn = reg_required_here (&str, 16)) == FAIL)
+ return;
+
+ if (rn == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ skip_whitespace (str);
+
+ if (*str == '!')
+ {
+ inst.instruction |= WRITE_BACK;
+ str++;
+ }
+ end_of_line (str);
+}
+
+/* ARM V6 REV (Byte Reverse Word) reverses the byte order in a 32-bit
+ register (argument parse).
+ REV{<cond>} Rd, Rm.
+ Condition defaults to COND_ALWAYS.
+ Error if Rd or Rm are R15. */
+
+static void
+do_rev (str)
+ char* str;
+{
+ int rd, rm;
+
+ skip_whitespace (str);
+
+ if ((rd = reg_required_here (&str, 12)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ else if (rd == REG_PC || rm == REG_PC)
+ inst.error = BAD_PC;
+
+ else
+ end_of_line (str);
+}
+
+/* ARM V6 Perform Two Sixteen Bit Integer Additions. (argument parse).
+ QADD16{<cond>} <Rd>, <Rn>, <Rm>
+ Condition defaults to COND_ALWAYS.
+ Error if Rd, Rn or Rm are R15. */
+
+static void
+do_qadd16 (str)
+ char* str;
+{
+ int rd, rm, rn;
+
+ skip_whitespace (str);
+
+ if ((rd = reg_required_here (&str, 12)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rn = reg_required_here (&str, 16)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ else if (rd == REG_PC || rm == REG_PC || rn == REG_PC)
+ inst.error = BAD_PC;
+
+ else
+ end_of_line (str);
+}
+
+/* ARM V6 Pack Halfword Bottom Top instruction (argument parse).
+ PKHBT {<cond>} <Rd>, <Rn>, <Rm> {, LSL #<shift_imm>}
+ Condition defaults to COND_ALWAYS.
+ Error if Rd, Rn or Rm are R15. */
+
+static void
+do_pkhbt (str)
+ char* str;
+{
+ do_pkh_core (str, SHIFT_LSL_IMMEDIATE);
+}
+
+/* ARM V6 PKHTB (Argument Parse). */
+
+static void
+do_pkhtb (str)
+ char* str;
+{
+ do_pkh_core (str, SHIFT_ASR_IMMEDIATE);
+}
+
+static void
+do_pkh_core (str, shift)
+ char* str;
+ int shift;
+{
+ int rd, rn, rm;
+
+ skip_whitespace (str);
+ if (((rd = reg_required_here (&str, 12)) == FAIL)
+ || (skip_past_comma (&str) == FAIL)
+ || ((rn = reg_required_here (&str, 16)) == FAIL)
+ || (skip_past_comma (&str) == FAIL)
+ || ((rm = reg_required_here (&str, 0)) == FAIL))
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ else if (rd == REG_PC || rn == REG_PC || rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ /* Check for optional shift immediate constant. */
+ if (skip_past_comma (&str) == FAIL)
+ {
+ if (shift == SHIFT_ASR_IMMEDIATE)
+ {
+ /* If the shift specifier is ommited, turn the instruction
+ into pkhbt rd, rm, rn. First, switch the instruction
+ code, and clear the rn and rm fields. */
+ inst.instruction &= 0xfff0f010;
+ /* Now, re-encode the registers. */
+ inst.instruction |= (rm << 16) | rn;
+ }
+ return;
+ }
+
+ decode_shift (&str, shift);
+}
+
+/* ARM V6 Load Register Exclusive instruction (argument parse).
+ LDREX{<cond>} <Rd, [<Rn>]
+ Condition defaults to COND_ALWAYS.
+ Error if Rd or Rn are R15.
+ See ARMARMv6 A4.1.27: LDREX. */
+
+
+static void
+do_ldrex (str)
+ char * str;
+{
+ int rd, rn;
+
+ skip_whitespace (str);
+
+ /* Parse Rd. */
+ if (((rd = reg_required_here (&str, 12)) == FAIL)
+ || (skip_past_comma (&str) == FAIL))
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ else if (rd == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+ skip_whitespace (str);
+
+ /* Skip past '['. */
+ if ((strlen (str) >= 1)
+ &&strncmp (str, "[", 1) == 0)
+ str+=1;
+ skip_whitespace (str);
+
+ /* Parse Rn. */
+ if ((rn = reg_required_here (&str, 16)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+ else if (rn == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+ skip_whitespace (str);
+
+ /* Skip past ']'. */
+ if ((strlen (str) >= 1)
+ && strncmp (str, "]", 1) == 0)
+ str+=1;
+
+ end_of_line (str);
+}
+
+/* ARM V6 change processor state instruction (argument parse)
+ CPS, CPSIE, CSPID . */
+
+static void
+do_cps (str)
+ char * str;
+{
+ do_cps_mode (&str);
+ end_of_line (str);
+}
+
+static void
+do_cpsi (str)
+ char * str;
+{
+ do_cps_flags (&str, /*thumb_p=*/0);
+
+ if (skip_past_comma (&str) == SUCCESS)
+ {
+ skip_whitespace (str);
+ do_cps_mode (&str);
+ }
end_of_line (str);
}
-/* ARM V5 (argument parse)
- MCR2 <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
- MRC2 <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
- Instruction is not conditional, and has 0xf in the condition field.
- Otherwise, it's the same as MCR/MRC. */
-
static void
-do_co_reg2 (str)
- char * str;
+do_cps_mode (str)
+ char **str;
{
- skip_whitespace (str);
+ expressionS expr;
- if (co_proc_number (& str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ skip_whitespace (*str);
- if (skip_past_comma (& str) == FAIL
- || cp_opc_expr (& str, 21, 3) == FAIL)
+ if (! is_immediate_prefix (**str))
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ inst.error = _("immediate expression expected");
return;
}
- if (skip_past_comma (& str) == FAIL
- || reg_required_here (& str, 12) == FAIL)
+ (*str)++; /* Strip off the immediate signifier. */
+ if (my_get_expression (&expr, str))
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ inst.error = _("bad expression");
return;
}
- if (skip_past_comma (& str) == FAIL
- || cp_reg_required_here (& str, 16) == FAIL)
+ if (expr.X_op != O_constant)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ inst.error = _("constant expression expected");
return;
}
-
- if (skip_past_comma (& str) == FAIL
- || cp_reg_required_here (& str, 0) == FAIL)
+
+ /* The mode is a 5 bit field. Valid values are 0-31. */
+ if (((unsigned) expr.X_add_number) > 31
+ || (inst.reloc.exp.X_add_number) < 0)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ inst.error = _("invalid constant");
return;
}
-
- if (skip_past_comma (& str) == SUCCESS)
- {
- if (cp_opc_expr (& str, 5, 3) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
- }
-
- end_of_line (str);
+
+ inst.instruction |= expr.X_add_number;
}
-/* ARM v5TEJ. Jump to Jazelle code. */
static void
-do_bxj (str)
- char * str;
+do_cps_flags (str, thumb_p)
+ char **str;
+ int thumb_p;
{
- int reg;
+ 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 }
+ };
- skip_whitespace (str);
+ int saw_a_flag = 0;
- if ((reg = reg_required_here (&str, 0)) == FAIL)
+ skip_whitespace (*str);
+
+ /* Get the a, f and i flags. */
+ while (**str && **str != ',')
{
- inst.error = BAD_ARGS;
- return;
+ 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)++;
}
-
- /* Note - it is not illegal to do a "bxj pc". Useless, but not illegal. */
- if (reg == REG_PC)
- as_tsktsk (_("use of r15 in bxj is not really useful"));
-
- end_of_line (str);
+ if (!saw_a_flag)
+ inst.error = _("no 'a', 'i', or 'f' flags for 'cps'");
}
/* THUMB V5 breakpoint instruction (argument parse)
end_of_line (str);
}
+/* THUMB CPS instruction (argument parse). */
+
+static void
+do_t_cps (str)
+ char *str;
+{
+ do_cps_flags (&str, /*thumb_p=*/1);
+ end_of_line (str);
+}
+
+/* THUMB CPY instruction (argument parse). */
+
+static void
+do_t_cpy (str)
+ char *str;
+{
+ thumb_mov_compare (str, THUMB_CPY);
+}
+
+/* THUMB SETEND instruction (argument parse). */
+
+static void
+do_t_setend (str)
+ char *str;
+{
+ if (do_endian_specifier (str))
+ inst.instruction |= 0x8;
+}
+
static unsigned long check_iwmmxt_insn PARAMS ((char *, enum iwmmxt_insn_type, int));
/* Parse INSN_TYPE insn STR having a possible IMMEDIATE_SIZE immediate. */
if (reg != REG_PC && !inst.error)
inst.error = _("only r15 allowed here");
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_tbcst, 0);
-
- return;
}
static void
return;
inst.instruction |= number & 0x7;
- return;
}
static void
return;
inst.instruction |= number & 0x7;
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_tmcr, 0);
-
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_tmcrr, 0);
-
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_tmia, 0);
-
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_tmovmsk, 0);
-
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_tmrc, 0);
-
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_tmrrc, 0);
-
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_rd, 0);
- return;
}
static void
return;
inst.instruction |= ((number & 0x7) << 20);
- return;
}
static void
return;
inst.instruction |= ((inst.instruction >> 16) & 0xf);
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_wrwr, 0);
-
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_wrwrwcg, 0);
-
- return;
}
static void
char * str;
{
check_iwmmxt_insn (str, check_wrwrwr, 0);
-
- return;
}
static void
return;
inst.instruction |= ((number & 0xf0) << 16) | (number & 0xf);
- return;
}
static void
return;
inst.instruction |= ((inst.instruction & 0xf) << 12) | ((inst.instruction & 0xf) << 16);
- return;
}
/* Xscale multiply-accumulate (argument parse)
}
}
-/* UNRESTRICT should be one if <shift> <register> is permitted for this
- instruction. */
+/* KIND indicates what kind of shifts are accepted. */
static int
-decode_shift (str, unrestrict)
+decode_shift (str, kind)
char ** str;
- int unrestrict;
+ int kind;
{
const struct asm_shift_name * shift;
char * p;
assert (shift->properties->index == shift_properties[shift->properties->index].index);
+ if (kind == SHIFT_LSL_OR_ASR_IMMEDIATE
+ && shift->properties->index != SHIFT_LSL
+ && shift->properties->index != SHIFT_ASR)
+ {
+ inst.error = _("'LSL' or 'ASR' required");
+ return FAIL;
+ }
+ else if (kind == SHIFT_LSL_IMMEDIATE
+ && shift->properties->index != SHIFT_LSL)
+ {
+ inst.error = _("'LSL' required");
+ return FAIL;
+ }
+ else if (kind == SHIFT_ASR_IMMEDIATE
+ && shift->properties->index != SHIFT_ASR)
+ {
+ inst.error = _("'ASR' required");
+ return FAIL;
+ }
+
if (shift->properties->index == SHIFT_RRX)
{
* str = p;
skip_whitespace (p);
- if (unrestrict && reg_required_here (& p, 8) != FAIL)
+ if (kind == NO_SHIFT_RESTRICT && reg_required_here (& p, 8) != FAIL)
{
inst.instruction |= shift->properties->bit_field | SHIFT_BY_REG;
* str = p;
}
else if (! is_immediate_prefix (* p))
{
- inst.error = (unrestrict
+ inst.error = (NO_SHIFT_RESTRICT
? _("shift requires register or #expression")
: _("shift requires #expression"));
* str = p;
}
end_of_line (str);
- return;
}
static void
/* Frag hacking will turn this into a sub instruction if the offset turns
out to be negative. */
inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+#ifndef TE_WINCE
inst.reloc.exp.X_add_number -= 8; /* PC relative adjust. */
+#endif
inst.reloc.pc_rel = 1;
end_of_line (str);
/* Frag hacking will turn this into a sub instruction if the offset turns
out to be negative. */
inst.reloc.type = BFD_RELOC_ARM_ADRL_IMMEDIATE;
+#ifndef TE_WINCE
inst.reloc.exp.X_add_number -= 8; /* PC relative adjust */
+#endif
inst.reloc.pc_rel = 1;
inst.size = INSN_SIZE * 2;
-
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static int
inst.instruction |= add | OFFSET_REG;
if (skip_past_comma (str) == SUCCESS)
- return decode_shift (str, SHIFT_RESTRICT);
+ return decode_shift (str, SHIFT_IMMEDIATE);
return SUCCESS;
}
inst.instruction |= (pre_inc ? PRE_INDEX : 0);
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static int
inst.instruction |= (pre_inc ? PRE_INDEX : 0);
end_of_line (str);
- return;
}
static long
inst.instruction |= range;
end_of_line (str);
- return;
}
static void
inst.reloc.type = BFD_RELOC_ARM_SWI;
inst.reloc.pc_rel = 0;
end_of_line (str);
-
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
#endif /* OBJ_ELF */
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static int
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
-do_vfp_sp_reg2 (str)
+do_vfp_reg2_from_sp2 (str)
char *str;
{
skip_whitespace (str);
- if (reg_required_here (&str, 12) == FAIL)
- return;
-
- if (skip_past_comma (&str) == FAIL
+ if (reg_required_here (&str, 12) == FAIL
+ || skip_past_comma (&str) == FAIL
|| reg_required_here (&str, 16) == FAIL
|| skip_past_comma (&str) == FAIL)
{
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
+}
+
+static void
+do_vfp_sp2_from_reg2 (str)
+ char *str;
+{
+ skip_whitespace (str);
+
+ /* We require exactly two consecutive SP registers. */
+ if (vfp_sp_reg_list (&str, VFP_REG_Sm) != 2)
+ {
+ if (! inst.error)
+ inst.error = _("only two consecutive VFP SP registers allowed here");
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || reg_required_here (&str, 12) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || reg_required_here (&str, 16) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ end_of_line (str);
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
if (skip_past_comma (&str) == FAIL
|| reg_required_here (&str, 12) == FAIL
|| skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 16))
+ || reg_required_here (&str, 16) == FAIL)
{
if (! inst.error)
inst.error = BAD_ARGS;
}
end_of_line (str);
- return;
}
static const struct vfp_reg *
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
/* Parse and encode a VFP SP register list, storing the initial
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
static void
}
end_of_line (str);
- return;
}
/* Thumb specific routines. */
return;
}
- if (is_immediate_prefix (*str))
+ if (move != THUMB_CPY && is_immediate_prefix (*str))
{
str++;
if (my_get_expression (&inst.reloc.exp, &str))
if (Rs != FAIL)
{
- if (Rs < 8 && Rd < 8)
+ if (move != THUMB_CPY && Rs < 8 && Rd < 8)
{
if (move == THUMB_MOVE)
/* A move of two lowregs is encoded as ADD Rd, Rs, #0
{
if (move == THUMB_MOVE)
inst.instruction = T_OPCODE_MOV_HR;
- else
+ else if (move != THUMB_CPY)
inst.instruction = T_OPCODE_CMP_HR;
if (Rd > 7)
fail_ldst:
if (!inst.error)
inst.error = BAD_ARGS;
- return;
}
static void
{
/* Do nothing. */
end_of_line (str);
- return;
}
/* Handle the Format 4 instructions that do not have equivalents in other
inst.reloc.type = BFD_RELOC_ARM_SWI;
end_of_line (str);
- return;
}
static void
if (uses_apcs_float) flags |= F_APCS_FLOAT;
if (pic_code) flags |= F_PIC;
if ((cpu_variant & FPU_ANY) == FPU_NONE
- || (cpu_variant & FPU_ANY) == FPU_ARCH_VFP) /* VFP layout only. */
- flags |= F_SOFT_FLOAT;
+ || (cpu_variant & FPU_ANY) == FPU_ARCH_VFP) /* VFP layout only. */
+ {
+ flags |= F_SOFT_FLOAT;
+ }
+ switch (mfloat_abi_opt)
+ {
+ case ARM_FLOAT_ABI_SOFT:
+ case ARM_FLOAT_ABI_SOFTFP:
+ flags |= F_SOFT_FLOAT;
+ break;
+
+ case ARM_FLOAT_ABI_HARD:
+ if (flags & F_SOFT_FLOAT)
+ as_bad (_("hard-float conflicts with specified fpu"));
+ break;
+ }
/* Using VFP conventions (even if soft-float). */
if (cpu_variant & FPU_VFP_EXT_NONE) flags |= F_VFP_FLOAT;
#if defined OBJ_ELF
- if (cpu_variant & ARM_CEXT_MAVERICK)
- {
- flags &= ~ F_SOFT_FLOAT;
+ if (cpu_variant & FPU_ARCH_MAVERICK)
flags |= EF_ARM_MAVERICK_FLOAT;
- }
#endif
bfd_set_private_flags (stdoutput, flags);
return NULL;
case BFD_RELOC_ARM_OFFSET_IMM:
+ if (fixp->fx_addsy != NULL
+ && !S_IS_DEFINED (fixp->fx_addsy)
+ && S_IS_LOCAL (fixp->fx_addsy))
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("undefined local label `%s'"),
+ S_GET_NAME (fixp->fx_addsy));
+ return NULL;
+ }
+
as_bad_where (fixp->fx_file, fixp->fx_line,
_("internal_relocation (type: OFFSET_IMM) not fixed up"));
return NULL;
{"arm9e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
{"arm9e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
{"arm926ej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
+ {"arm926ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
{"arm946e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
{"arm946e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
{"arm966e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
{"arm1020", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
{"arm1020t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1},
{"arm1020e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
+ {"arm1026ejs", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
+ {"arm1136js", ARM_ARCH_V6, FPU_NONE},
+ {"arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2},
/* ??? XSCALE is really an architecture. */
{"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2},
/* ??? iwmmxt is not a processor. */
{"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2},
{"i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2},
/* Maverick */
- {"ep9312", ARM_ARCH_V4T | ARM_CEXT_MAVERICK, FPU_NONE},
+ {"ep9312", ARM_ARCH_V4T | ARM_CEXT_MAVERICK, FPU_ARCH_MAVERICK},
{NULL, 0, 0}
};
{"armv5te", ARM_ARCH_V5TE, FPU_ARCH_VFP},
{"armv5texp", ARM_ARCH_V5TExP, FPU_ARCH_VFP},
{"armv5tej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP},
+ {"armv6", ARM_ARCH_V6, FPU_ARCH_VFP},
+ {"armv6j", ARM_ARCH_V6, FPU_ARCH_VFP},
{"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP},
{"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP},
{NULL, 0, 0}
{"vfpxd", FPU_ARCH_VFP_V1xD},
{"arm1020t", FPU_ARCH_VFP_V1},
{"arm1020e", FPU_ARCH_VFP_V2},
+ {"arm1136jfs", FPU_ARCH_VFP_V2},
+ {"maverick", FPU_ARCH_MAVERICK},
+ {NULL, 0}
+};
+
+struct arm_float_abi_option_table
+{
+ char *name;
+ int value;
+};
+
+static struct arm_float_abi_option_table arm_float_abis[] =
+{
+ {"hard", ARM_FLOAT_ABI_HARD},
+ {"softfp", ARM_FLOAT_ABI_SOFTFP},
+ {"soft", ARM_FLOAT_ABI_SOFT},
{NULL, 0}
};
return 0;
}
+static int
+arm_parse_float_abi (str)
+ char * str;
+{
+ struct arm_float_abi_option_table *opt;
+
+ for (opt = arm_float_abis; opt->name != NULL; opt++)
+ if (strcmp (opt->name, str) == 0)
+ {
+ mfloat_abi_opt = opt->value;
+ return 1;
+ }
+
+ as_bad (_("unknown floating point abi `%s'\n"), str);
+ return 0;
+}
+
struct arm_long_option_table arm_long_opts[] =
{
{"mcpu=", N_("<cpu name>\t assemble for CPU <cpu name>"),
arm_parse_arch, NULL},
{"mfpu=", N_("<fpu name>\t assemble for FPU architecture <fpu name>"),
arm_parse_fpu, NULL},
+ {"mfloat-abi=", N_("<abi>\t assemble for floating point ABI <abi>"),
+ arm_parse_float_abi, NULL},
{NULL, NULL, 0, NULL}
};
arm_data = (arm_fix_data *) obstack_alloc (& notes, sizeof (arm_fix_data));
new_fix->tc_fix_data = (PTR) arm_data;
arm_data->thumb_mode = thumb_mode;
-
- return;
}
/* This fix_new is called by cons via TC_CONS_FIX_NEW. */