tc-arm.c revision 68765
160484Sobrien/* tc-arm.c -- Assemble for the ARM
260484Sobrien   Copyright (C) 1994, 95, 96, 97, 98, 1999, 2000 Free Software Foundation, Inc.
360484Sobrien   Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
460484Sobrien	Modified by David Taylor (dtaylor@armltd.co.uk)
560484Sobrien
660484Sobrien   This file is part of GAS, the GNU Assembler.
760484Sobrien
860484Sobrien   GAS is free software; you can redistribute it and/or modify
960484Sobrien   it under the terms of the GNU General Public License as published by
1060484Sobrien   the Free Software Foundation; either version 2, or (at your option)
1160484Sobrien   any later version.
1260484Sobrien
1360484Sobrien   GAS is distributed in the hope that it will be useful,
1460484Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
1560484Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1660484Sobrien   GNU General Public License for more details.
1760484Sobrien
1860484Sobrien   You should have received a copy of the GNU General Public License
1960484Sobrien   along with GAS; see the file COPYING.  If not, write to the Free
2060484Sobrien   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
2160484Sobrien   02111-1307, USA.  */
2260484Sobrien
2360484Sobrien#include <ctype.h>
2460484Sobrien#include <string.h>
2560484Sobrien#define  NO_RELOC 0
2660484Sobrien#include "as.h"
2760484Sobrien
2860484Sobrien/* need TARGET_CPU */
2960484Sobrien#include "config.h"
3060484Sobrien#include "subsegs.h"
3160484Sobrien#include "obstack.h"
3260484Sobrien#include "symbols.h"
3360484Sobrien#include "listing.h"
3460484Sobrien
3560484Sobrien#ifdef OBJ_ELF
3660484Sobrien#include "elf/arm.h"
3760484Sobrien#endif
3860484Sobrien
3960484Sobrien/* Types of processor to assemble for.  */
4060484Sobrien#define ARM_1		0x00000001
4160484Sobrien#define ARM_2		0x00000002
4260484Sobrien#define ARM_3		0x00000004
4360484Sobrien#define ARM_250		ARM_3
4460484Sobrien#define ARM_6		0x00000008
4560484Sobrien#define ARM_7		ARM_6           /* same core instruction set */
4660484Sobrien#define ARM_8		ARM_6           /* same core instruction set */
4760484Sobrien#define ARM_9		ARM_6           /* same core instruction set */
4860484Sobrien#define ARM_CPU_MASK	0x0000000f
4960484Sobrien
5060484Sobrien/* The following bitmasks control CPU extensions (ARM7 onwards): */
5160484Sobrien#define ARM_LONGMUL	0x00000010	/* allow long multiplies */
5260484Sobrien#define ARM_HALFWORD    0x00000020	/* allow half word loads */
5360484Sobrien#define ARM_THUMB       0x00000040	/* allow BX instruction  */
5460484Sobrien#define ARM_EXT_V5	0x00000080	/* allow CLZ etc	 */
5560484Sobrien#define ARM_EXT_V5E     0x00000200	/* "El Segundo" 	 */
5660484Sobrien
5760484Sobrien/* Architectures are the sum of the base and extensions.  */
5860484Sobrien#define ARM_ARCH_V4	(ARM_7 | ARM_LONGMUL | ARM_HALFWORD)
5960484Sobrien#define ARM_ARCH_V4T	(ARM_ARCH_V4 | ARM_THUMB)
6060484Sobrien#define ARM_ARCH_V5	(ARM_ARCH_V4 | ARM_EXT_V5)
6160484Sobrien#define ARM_ARCH_V5T	(ARM_ARCH_V5 | ARM_THUMB)
6260484Sobrien
6360484Sobrien/* Some useful combinations:  */
6460484Sobrien#define ARM_ANY		0x00ffffff
6560484Sobrien#define ARM_2UP		(ARM_ANY - ARM_1)
6660484Sobrien#define ARM_ALL		ARM_2UP		/* Not arm1 only */
6760484Sobrien#define ARM_3UP		0x00fffffc
6860484Sobrien#define ARM_6UP		0x00fffff8      /* Includes ARM7 */
6960484Sobrien
7060484Sobrien#define FPU_CORE	0x80000000
7160484Sobrien#define FPU_FPA10	0x40000000
7260484Sobrien#define FPU_FPA11	0x40000000
7360484Sobrien#define FPU_NONE	0
7460484Sobrien
7560484Sobrien/* Some useful combinations  */
7660484Sobrien#define FPU_ALL		0xff000000	/* Note this is ~ARM_ANY */
7760484Sobrien#define FPU_MEMMULTI	0x7f000000	/* Not fpu_core */
7860484Sobrien
7960484Sobrien
8060484Sobrien#ifndef CPU_DEFAULT
8160484Sobrien#if defined __thumb__
8260484Sobrien#define CPU_DEFAULT (ARM_ARCH_V4 | ARM_THUMB)
8360484Sobrien#else
8460484Sobrien#define CPU_DEFAULT ARM_ALL
8560484Sobrien#endif
8660484Sobrien#endif
8760484Sobrien
8860484Sobrien#ifndef FPU_DEFAULT
8960484Sobrien#define FPU_DEFAULT FPU_ALL
9060484Sobrien#endif
9160484Sobrien
9260484Sobrien#define streq(a, b)           (strcmp (a, b) == 0)
9360484Sobrien#define skip_whitespace(str)  while (* (str) == ' ') ++ (str)
9460484Sobrien
9560484Sobrienstatic unsigned long	cpu_variant = CPU_DEFAULT | FPU_DEFAULT;
9660484Sobrienstatic int target_oabi = 0;
9760484Sobrien
9860484Sobrien#if defined OBJ_COFF || defined OBJ_ELF
9960484Sobrien/* Flags stored in private area of BFD structure */
10060484Sobrienstatic boolean		uses_apcs_26 = false;
10160484Sobrienstatic boolean		support_interwork = false;
10260484Sobrienstatic boolean		uses_apcs_float = false;
10360484Sobrienstatic boolean		pic_code = false;
10460484Sobrien#endif
10560484Sobrien
10660484Sobrien/* This array holds the chars that always start a comment.  If the
10760484Sobrien   pre-processor is disabled, these aren't very useful.  */
10860484SobrienCONST char comment_chars[] = "@";
10960484Sobrien
11060484Sobrien/* This array holds the chars that only start a comment at the beginning of
11160484Sobrien   a line.  If the line seems to have the form '# 123 filename'
11260484Sobrien   .line and .file directives will appear in the pre-processed output.  */
11360484Sobrien/* Note that input_file.c hand checks for '#' at the beginning of the
11460484Sobrien   first line of the input file.  This is because the compiler outputs
11560484Sobrien   #NO_APP at the beginning of its output.  */
11660484Sobrien/* Also note that comments like this one will always work.  */
11760484SobrienCONST char line_comment_chars[] = "#";
11860484Sobrien
11960484Sobrien#ifdef TE_LINUX
12060484SobrienCONST char line_separator_chars[] = ";";
12160484Sobrien#else
12260484SobrienCONST char line_separator_chars[] = "";
12360484Sobrien#endif
12460484Sobrien
12560484Sobrien/* Chars that can be used to separate mant
12660484Sobrien   from exp in floating point numbers.  */
12760484SobrienCONST char EXP_CHARS[] = "eE";
12860484Sobrien
12960484Sobrien/* Chars that mean this number is a floating point constant */
13060484Sobrien/* As in 0f12.456 */
13160484Sobrien/* or    0d1.2345e12 */
13260484Sobrien
13360484SobrienCONST char FLT_CHARS[] = "rRsSfFdDxXeEpP";
13460484Sobrien
13560484Sobrien/* Prefix characters that indicate the start of an immediate
13660484Sobrien   value.  */
13760484Sobrien#define is_immediate_prefix(C) ((C) == '#' || (C) == '$')
13860484Sobrien
13960484Sobrien#ifdef OBJ_ELF
14060484SobriensymbolS * GOT_symbol;		/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
14160484Sobrien#endif
14260484Sobrien
14360484SobrienCONST int md_reloc_size = 8;	/* Size of relocation record */
14460484Sobrien
14560484Sobrienstatic int thumb_mode = 0;      /* 0: assemble for ARM, 1: assemble for Thumb,
14660484Sobrien				   2: assemble for Thumb even though target cpu
14760484Sobrien				   does not support thumb instructions.  */
14860484Sobrientypedef struct arm_fix
14960484Sobrien{
15060484Sobrien  int thumb_mode;
15160484Sobrien} arm_fix_data;
15260484Sobrien
15360484Sobrienstruct arm_it
15460484Sobrien{
15560484Sobrien  CONST char *  error;
15660484Sobrien  unsigned long instruction;
15760484Sobrien  int           suffix;
15860484Sobrien  int           size;
15960484Sobrien  struct
16060484Sobrien    {
16160484Sobrien      bfd_reloc_code_real_type type;
16260484Sobrien      expressionS              exp;
16360484Sobrien      int                      pc_rel;
16460484Sobrien    } reloc;
16560484Sobrien};
16660484Sobrien
16760484Sobrienstruct arm_it inst;
16860484Sobrien
16960484Sobrienstruct asm_shift
17060484Sobrien{
17160484Sobrien  CONST char *  template;
17260484Sobrien  unsigned long value;
17360484Sobrien};
17460484Sobrien
17560484Sobrienstatic CONST struct asm_shift shift[] =
17660484Sobrien{
17760484Sobrien  {"asl", 0},
17860484Sobrien  {"lsl", 0},
17960484Sobrien  {"lsr", 0x00000020},
18060484Sobrien  {"asr", 0x00000040},
18160484Sobrien  {"ror", 0x00000060},
18260484Sobrien  {"rrx", 0x00000060},
18360484Sobrien  {"ASL", 0},
18460484Sobrien  {"LSL", 0},
18560484Sobrien  {"LSR", 0x00000020},
18660484Sobrien  {"ASR", 0x00000040},
18760484Sobrien  {"ROR", 0x00000060},
18860484Sobrien  {"RRX", 0x00000060}
18960484Sobrien};
19060484Sobrien
19160484Sobrien#define NO_SHIFT_RESTRICT 1
19260484Sobrien#define SHIFT_RESTRICT	  0
19360484Sobrien
19460484Sobrien#define NUM_FLOAT_VALS 8
19560484Sobrien
19660484SobrienCONST char * fp_const[] =
19760484Sobrien{
19860484Sobrien  "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0
19960484Sobrien};
20060484Sobrien
20160484Sobrien/* Number of littlenums required to hold an extended precision number.  */
20260484Sobrien#define MAX_LITTLENUMS 6
20360484Sobrien
20460484SobrienLITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
20560484Sobrien
20660484Sobrien#define FAIL	(-1)
20760484Sobrien#define SUCCESS (0)
20860484Sobrien
20960484Sobrien#define SUFF_S 1
21060484Sobrien#define SUFF_D 2
21160484Sobrien#define SUFF_E 3
21260484Sobrien#define SUFF_P 4
21360484Sobrien
21460484Sobrien#define CP_T_X   0x00008000
21560484Sobrien#define CP_T_Y   0x00400000
21660484Sobrien#define CP_T_Pre 0x01000000
21760484Sobrien#define CP_T_UD  0x00800000
21860484Sobrien#define CP_T_WB  0x00200000
21960484Sobrien
22060484Sobrien#define CONDS_BIT       (0x00100000)
22160484Sobrien#define LOAD_BIT        (0x00100000)
22260484Sobrien#define TRANS_BIT	(0x00200000)
22360484Sobrien
22460484Sobrienstruct asm_cond
22560484Sobrien{
22660484Sobrien  CONST char *  template;
22760484Sobrien  unsigned long value;
22860484Sobrien};
22960484Sobrien
23060484Sobrien/* This is to save a hash look-up in the common case.  */
23160484Sobrien#define COND_ALWAYS 0xe0000000
23260484Sobrien
23360484Sobrienstatic CONST struct asm_cond conds[] =
23460484Sobrien{
23560484Sobrien  {"eq", 0x00000000},
23660484Sobrien  {"ne", 0x10000000},
23760484Sobrien  {"cs", 0x20000000}, {"hs", 0x20000000},
23860484Sobrien  {"cc", 0x30000000}, {"ul", 0x30000000}, {"lo", 0x30000000},
23960484Sobrien  {"mi", 0x40000000},
24060484Sobrien  {"pl", 0x50000000},
24160484Sobrien  {"vs", 0x60000000},
24260484Sobrien  {"vc", 0x70000000},
24360484Sobrien  {"hi", 0x80000000},
24460484Sobrien  {"ls", 0x90000000},
24560484Sobrien  {"ge", 0xa0000000},
24660484Sobrien  {"lt", 0xb0000000},
24760484Sobrien  {"gt", 0xc0000000},
24860484Sobrien  {"le", 0xd0000000},
24960484Sobrien  {"al", 0xe0000000},
25060484Sobrien  {"nv", 0xf0000000}
25160484Sobrien};
25260484Sobrien
25360484Sobrien/* Warning: If the top bit of the set_bits is set, then the standard
25460484Sobrien   instruction bitmask is ignored, and the new bitmask is taken from
25560484Sobrien   the set_bits:  */
25660484Sobrienstruct asm_flg
25760484Sobrien{
25860484Sobrien  CONST char *  template;	/* Basic flag string */
25960484Sobrien  unsigned long set_bits;	/* Bits to set */
26060484Sobrien};
26160484Sobrien
26260484Sobrienstatic CONST struct asm_flg s_flag[] =
26360484Sobrien{
26460484Sobrien  {"s", CONDS_BIT},
26560484Sobrien  {NULL, 0}
26660484Sobrien};
26760484Sobrien
26860484Sobrienstatic CONST struct asm_flg ldr_flags[] =
26960484Sobrien{
27060484Sobrien  {"b",  0x00400000},
27160484Sobrien  {"t",  TRANS_BIT},
27260484Sobrien  {"bt", 0x00400000 | TRANS_BIT},
27360484Sobrien  {"h",  0x801000b0},
27460484Sobrien  {"sh", 0x801000f0},
27560484Sobrien  {"sb", 0x801000d0},
27660484Sobrien  {NULL, 0}
27760484Sobrien};
27860484Sobrien
27960484Sobrienstatic CONST struct asm_flg str_flags[] =
28060484Sobrien{
28160484Sobrien  {"b",  0x00400000},
28260484Sobrien  {"t",  TRANS_BIT},
28360484Sobrien  {"bt", 0x00400000 | TRANS_BIT},
28460484Sobrien  {"h",  0x800000b0},
28560484Sobrien  {NULL, 0}
28660484Sobrien};
28760484Sobrien
28860484Sobrienstatic CONST struct asm_flg byte_flag[] =
28960484Sobrien{
29060484Sobrien  {"b", 0x00400000},
29160484Sobrien  {NULL, 0}
29260484Sobrien};
29360484Sobrien
29460484Sobrienstatic CONST struct asm_flg cmp_flags[] =
29560484Sobrien{
29660484Sobrien  {"s", CONDS_BIT},
29760484Sobrien  {"p", 0x0010f000},
29860484Sobrien  {NULL, 0}
29960484Sobrien};
30060484Sobrien
30160484Sobrienstatic CONST struct asm_flg ldm_flags[] =
30260484Sobrien{
30360484Sobrien  {"ed", 0x01800000},
30460484Sobrien  {"fd", 0x00800000},
30560484Sobrien  {"ea", 0x01000000},
30660484Sobrien  {"fa", 0x08000000},
30760484Sobrien  {"ib", 0x01800000},
30860484Sobrien  {"ia", 0x00800000},
30960484Sobrien  {"db", 0x01000000},
31060484Sobrien  {"da", 0x08000000},
31160484Sobrien  {NULL, 0}
31260484Sobrien};
31360484Sobrien
31460484Sobrienstatic CONST struct asm_flg stm_flags[] =
31560484Sobrien{
31660484Sobrien  {"ed", 0x08000000},
31760484Sobrien  {"fd", 0x01000000},
31860484Sobrien  {"ea", 0x00800000},
31960484Sobrien  {"fa", 0x01800000},
32060484Sobrien  {"ib", 0x01800000},
32160484Sobrien  {"ia", 0x00800000},
32260484Sobrien  {"db", 0x01000000},
32360484Sobrien  {"da", 0x08000000},
32460484Sobrien  {NULL, 0}
32560484Sobrien};
32660484Sobrien
32760484Sobrienstatic CONST struct asm_flg lfm_flags[] =
32860484Sobrien{
32960484Sobrien  {"fd", 0x00800000},
33060484Sobrien  {"ea", 0x01000000},
33160484Sobrien  {NULL, 0}
33260484Sobrien};
33360484Sobrien
33460484Sobrienstatic CONST struct asm_flg sfm_flags[] =
33560484Sobrien{
33660484Sobrien  {"fd", 0x01000000},
33760484Sobrien  {"ea", 0x00800000},
33860484Sobrien  {NULL, 0}
33960484Sobrien};
34060484Sobrien
34160484Sobrienstatic CONST struct asm_flg round_flags[] =
34260484Sobrien{
34360484Sobrien  {"p", 0x00000020},
34460484Sobrien  {"m", 0x00000040},
34560484Sobrien  {"z", 0x00000060},
34660484Sobrien  {NULL, 0}
34760484Sobrien};
34860484Sobrien
34960484Sobrien/* The implementation of the FIX instruction is broken on some assemblers,
35060484Sobrien   in that it accepts a precision specifier as well as a rounding specifier,
35160484Sobrien   despite the fact that this is meaningless.  To be more compatible, we
35260484Sobrien   accept it as well, though of course it does not set any bits.  */
35360484Sobrienstatic CONST struct asm_flg fix_flags[] =
35460484Sobrien{
35560484Sobrien  {"p", 0x00000020},
35660484Sobrien  {"m", 0x00000040},
35760484Sobrien  {"z", 0x00000060},
35860484Sobrien  {"sp", 0x00000020},
35960484Sobrien  {"sm", 0x00000040},
36060484Sobrien  {"sz", 0x00000060},
36160484Sobrien  {"dp", 0x00000020},
36260484Sobrien  {"dm", 0x00000040},
36360484Sobrien  {"dz", 0x00000060},
36460484Sobrien  {"ep", 0x00000020},
36560484Sobrien  {"em", 0x00000040},
36660484Sobrien  {"ez", 0x00000060},
36760484Sobrien  {NULL, 0}
36860484Sobrien};
36960484Sobrien
37060484Sobrienstatic CONST struct asm_flg except_flag[] =
37160484Sobrien{
37260484Sobrien  {"e", 0x00400000},
37360484Sobrien  {NULL, 0}
37460484Sobrien};
37560484Sobrien
37660484Sobrienstatic CONST struct asm_flg cplong_flag[] =
37760484Sobrien{
37860484Sobrien  {"l", 0x00400000},
37960484Sobrien  {NULL, 0}
38060484Sobrien};
38160484Sobrien
38260484Sobrienstruct asm_psr
38360484Sobrien{
38460484Sobrien  CONST char *  template;
38561843Sobrien  boolean       cpsr;
38661843Sobrien  unsigned long field;
38760484Sobrien};
38860484Sobrien
38961843Sobrien#define SPSR_BIT   (1 << 22)  /* The bit that distnguishes CPSR and SPSR.  */
39061843Sobrien#define PSR_SHIFT  16  /* How many bits to shift the PSR_xxx bits up by.  */
39160484Sobrien
39261843Sobrien#define PSR_c   (1 << 0)
39361843Sobrien#define PSR_x   (1 << 1)
39461843Sobrien#define PSR_s   (1 << 2)
39561843Sobrien#define PSR_f   (1 << 3)
39660484Sobrien
39760484Sobrienstatic CONST struct asm_psr psrs[] =
39860484Sobrien{
39961843Sobrien  {"CPSR",	true,  PSR_c | PSR_f},
40061843Sobrien  {"CPSR_all",	true,  PSR_c | PSR_f},
40161843Sobrien  {"SPSR",	false, PSR_c | PSR_f},
40261843Sobrien  {"SPSR_all",	false, PSR_c | PSR_f},
40361843Sobrien  {"CPSR_flg",	true,  PSR_f},
40461843Sobrien  {"CPSR_f",    true,  PSR_f},
40561843Sobrien  {"SPSR_flg",	false, PSR_f},
40661843Sobrien  {"SPSR_f",    false, PSR_f},
40761843Sobrien  {"CPSR_c",	true,  PSR_c},
40861843Sobrien  {"CPSR_ctl",	true,  PSR_c},
40961843Sobrien  {"SPSR_c",	false, PSR_c},
41061843Sobrien  {"SPSR_ctl",	false, PSR_c},
41161843Sobrien  {"CPSR_x",    true,  PSR_x},
41261843Sobrien  {"CPSR_s",    true,  PSR_s},
41361843Sobrien  {"SPSR_x",    false, PSR_x},
41461843Sobrien  {"SPSR_s",    false, PSR_s},
41561843Sobrien  /* For backwards compatability with older toolchain we also
41661843Sobrien     support lower case versions of some of these flags.  */
41761843Sobrien  {"cpsr",	true,  PSR_c | PSR_f},
41861843Sobrien  {"cpsr_all",	true,  PSR_c | PSR_f},
41961843Sobrien  {"spsr",	false, PSR_c | PSR_f},
42061843Sobrien  {"spsr_all",	false, PSR_c | PSR_f},
42161843Sobrien  {"cpsr_flg",	true,  PSR_f},
42261843Sobrien  {"cpsr_f",    true,  PSR_f},
42361843Sobrien  {"spsr_flg",	false, PSR_f},
42461843Sobrien  {"spsr_f",    false, PSR_f},
42561843Sobrien  {"cpsr_c",	true,  PSR_c},
42661843Sobrien  {"cpsr_ctl",	true,  PSR_c},
42761843Sobrien  {"spsr_c",	false, PSR_c},
42861843Sobrien  {"spsr_ctl",	false, PSR_c}
42960484Sobrien};
43060484Sobrien
43160484Sobrien/* Functions called by parser.  */
43260484Sobrien/* ARM instructions */
43360484Sobrienstatic void do_arit		PARAMS ((char *, unsigned long));
43460484Sobrienstatic void do_cmp		PARAMS ((char *, unsigned long));
43560484Sobrienstatic void do_mov		PARAMS ((char *, unsigned long));
43660484Sobrienstatic void do_ldst		PARAMS ((char *, unsigned long));
43760484Sobrienstatic void do_ldmstm		PARAMS ((char *, unsigned long));
43860484Sobrienstatic void do_branch		PARAMS ((char *, unsigned long));
43960484Sobrienstatic void do_swi		PARAMS ((char *, unsigned long));
44060484Sobrien/* Pseudo Op codes */
44160484Sobrienstatic void do_adr		PARAMS ((char *, unsigned long));
44260484Sobrienstatic void do_adrl		PARAMS ((char *, unsigned long));
44360484Sobrienstatic void do_nop		PARAMS ((char *, unsigned long));
44460484Sobrien/* ARM 2 */
44560484Sobrienstatic void do_mul		PARAMS ((char *, unsigned long));
44660484Sobrienstatic void do_mla		PARAMS ((char *, unsigned long));
44760484Sobrien/* ARM 3 */
44860484Sobrienstatic void do_swap		PARAMS ((char *, unsigned long));
44960484Sobrien/* ARM 6 */
45060484Sobrienstatic void do_msr		PARAMS ((char *, unsigned long));
45160484Sobrienstatic void do_mrs		PARAMS ((char *, unsigned long));
45260484Sobrien/* ARM 7M */
45360484Sobrienstatic void do_mull		PARAMS ((char *, unsigned long));
45460484Sobrien/* ARM THUMB */
45560484Sobrienstatic void do_bx               PARAMS ((char *, unsigned long));
45660484Sobrien
45760484Sobrien
45860484Sobrien/* Coprocessor Instructions */
45960484Sobrienstatic void do_cdp		PARAMS ((char *, unsigned long));
46060484Sobrienstatic void do_lstc		PARAMS ((char *, unsigned long));
46160484Sobrienstatic void do_co_reg		PARAMS ((char *, unsigned long));
46260484Sobrienstatic void do_fp_ctrl		PARAMS ((char *, unsigned long));
46360484Sobrienstatic void do_fp_ldst		PARAMS ((char *, unsigned long));
46460484Sobrienstatic void do_fp_ldmstm	PARAMS ((char *, unsigned long));
46560484Sobrienstatic void do_fp_dyadic	PARAMS ((char *, unsigned long));
46660484Sobrienstatic void do_fp_monadic	PARAMS ((char *, unsigned long));
46760484Sobrienstatic void do_fp_cmp		PARAMS ((char *, unsigned long));
46860484Sobrienstatic void do_fp_from_reg	PARAMS ((char *, unsigned long));
46960484Sobrienstatic void do_fp_to_reg	PARAMS ((char *, unsigned long));
47060484Sobrien
47160484Sobrienstatic void fix_new_arm		PARAMS ((fragS *, int, short, expressionS *, int, int));
47260484Sobrienstatic int arm_reg_parse	PARAMS ((char **));
47361843Sobrienstatic CONST struct asm_psr * arm_psr_parse PARAMS ((char **));
47460484Sobrienstatic void symbol_locate	PARAMS ((symbolS *, CONST char *, segT, valueT, fragS *));
47560484Sobrienstatic int add_to_lit_pool	PARAMS ((void));
47660484Sobrienstatic unsigned validate_immediate PARAMS ((unsigned));
47760484Sobrienstatic unsigned validate_immediate_twopart PARAMS ((unsigned int, unsigned int *));
47860484Sobrienstatic int validate_offset_imm	PARAMS ((unsigned int, int));
47960484Sobrienstatic void opcode_select	PARAMS ((int));
48060484Sobrienstatic void end_of_line		PARAMS ((char *));
48160484Sobrienstatic int reg_required_here	PARAMS ((char **, int));
48261843Sobrienstatic int psr_required_here	PARAMS ((char **));
48360484Sobrienstatic int co_proc_number	PARAMS ((char **));
48460484Sobrienstatic int cp_opc_expr		PARAMS ((char **, int, int));
48560484Sobrienstatic int cp_reg_required_here	PARAMS ((char **, int));
48660484Sobrienstatic int fp_reg_required_here	PARAMS ((char **, int));
48760484Sobrienstatic int cp_address_offset	PARAMS ((char **));
48860484Sobrienstatic int cp_address_required_here	PARAMS ((char **));
48960484Sobrienstatic int my_get_float_expression	PARAMS ((char **));
49060484Sobrienstatic int skip_past_comma	PARAMS ((char **));
49160484Sobrienstatic int walk_no_bignums	PARAMS ((symbolS *));
49260484Sobrienstatic int negate_data_op	PARAMS ((unsigned long *, unsigned long));
49360484Sobrienstatic int data_op2		PARAMS ((char **));
49460484Sobrienstatic int fp_op2		PARAMS ((char **));
49560484Sobrienstatic long reg_list		PARAMS ((char **));
49660484Sobrienstatic void thumb_load_store	PARAMS ((char *, int, int));
49760484Sobrienstatic int decode_shift		PARAMS ((char **, int));
49860484Sobrienstatic int ldst_extend		PARAMS ((char **, int));
49960484Sobrienstatic void thumb_add_sub	PARAMS ((char *, int));
50060484Sobrienstatic void insert_reg		PARAMS ((int));
50160484Sobrienstatic void thumb_shift		PARAMS ((char *, int));
50260484Sobrienstatic void thumb_mov_compare	PARAMS ((char *, int));
50360484Sobrienstatic void set_constant_flonums	PARAMS ((void));
50460484Sobrienstatic valueT md_chars_to_number	PARAMS ((char *, int));
50560484Sobrienstatic void insert_reg_alias	PARAMS ((char *, int));
50660484Sobrienstatic void output_inst		PARAMS ((void));
50760484Sobrien#ifdef OBJ_ELF
50860484Sobrienstatic bfd_reloc_code_real_type	arm_parse_reloc PARAMS ((void));
50960484Sobrien#endif
51060484Sobrien
51160484Sobrien/* ARM instructions take 4bytes in the object file, Thumb instructions
51260484Sobrien   take 2:  */
51360484Sobrien#define INSN_SIZE       4
51460484Sobrien
51560484Sobrien/* LONGEST_INST is the longest basic instruction name without conditions or
51660484Sobrien   flags.  ARM7M has 4 of length 5.  */
51760484Sobrien
51860484Sobrien#define LONGEST_INST 5
51960484Sobrien
52060484Sobrien
52160484Sobrienstruct asm_opcode
52260484Sobrien{
52360484Sobrien  CONST char *           template;	/* Basic string to match */
52460484Sobrien  unsigned long          value;		/* Basic instruction code */
52560484Sobrien
52660484Sobrien  /* Compulsory suffix that must follow conds. If "", then the
52760484Sobrien     instruction is not conditional and must have no suffix. */
52860484Sobrien  CONST char *           comp_suffix;
52960484Sobrien
53060484Sobrien  CONST struct asm_flg * flags;	        /* Bits to toggle if flag 'n' set */
53160484Sobrien  unsigned long          variants;	/* Which CPU variants this exists for */
53260484Sobrien  /* Function to call to parse args */
53360484Sobrien  void (*                parms) PARAMS ((char *, unsigned long));
53460484Sobrien};
53560484Sobrien
53660484Sobrienstatic CONST struct asm_opcode insns[] =
53760484Sobrien{
53860484Sobrien/* ARM Instructions */
53960484Sobrien  {"and",   0x00000000, NULL,   s_flag,      ARM_ANY,      do_arit},
54060484Sobrien  {"eor",   0x00200000, NULL,   s_flag,      ARM_ANY,      do_arit},
54160484Sobrien  {"sub",   0x00400000, NULL,   s_flag,      ARM_ANY,      do_arit},
54260484Sobrien  {"rsb",   0x00600000, NULL,   s_flag,      ARM_ANY,      do_arit},
54360484Sobrien  {"add",   0x00800000, NULL,   s_flag,      ARM_ANY,      do_arit},
54460484Sobrien  {"adc",   0x00a00000, NULL,   s_flag,      ARM_ANY,      do_arit},
54560484Sobrien  {"sbc",   0x00c00000, NULL,   s_flag,      ARM_ANY,      do_arit},
54660484Sobrien  {"rsc",   0x00e00000, NULL,   s_flag,      ARM_ANY,      do_arit},
54760484Sobrien  {"orr",   0x01800000, NULL,   s_flag,      ARM_ANY,      do_arit},
54860484Sobrien  {"bic",   0x01c00000, NULL,   s_flag,      ARM_ANY,      do_arit},
54960484Sobrien  {"tst",   0x01000000, NULL,   cmp_flags,   ARM_ANY,      do_cmp},
55060484Sobrien  {"teq",   0x01200000, NULL,   cmp_flags,   ARM_ANY,      do_cmp},
55160484Sobrien  {"cmp",   0x01400000, NULL,   cmp_flags,   ARM_ANY,      do_cmp},
55260484Sobrien  {"cmn",   0x01600000, NULL,   cmp_flags,   ARM_ANY,      do_cmp},
55360484Sobrien  {"mov",   0x01a00000, NULL,   s_flag,      ARM_ANY,      do_mov},
55460484Sobrien  {"mvn",   0x01e00000, NULL,   s_flag,      ARM_ANY,      do_mov},
55560484Sobrien  {"str",   0x04000000, NULL,   str_flags,   ARM_ANY,      do_ldst},
55660484Sobrien  {"ldr",   0x04100000, NULL,   ldr_flags,   ARM_ANY,      do_ldst},
55760484Sobrien  {"stm",   0x08000000, NULL,   stm_flags,   ARM_ANY,      do_ldmstm},
55860484Sobrien  {"ldm",   0x08100000, NULL,   ldm_flags,   ARM_ANY,      do_ldmstm},
55960484Sobrien  {"swi",   0x0f000000, NULL,   NULL,        ARM_ANY,      do_swi},
56060484Sobrien#ifdef TE_WINCE
56160484Sobrien  {"bl",    0x0b000000, NULL,   NULL,        ARM_ANY,      do_branch},
56260484Sobrien  {"b",     0x0a000000, NULL,   NULL,        ARM_ANY,      do_branch},
56360484Sobrien#else
56460484Sobrien  {"bl",    0x0bfffffe, NULL,   NULL,        ARM_ANY,      do_branch},
56560484Sobrien  {"b",     0x0afffffe, NULL,   NULL,        ARM_ANY,      do_branch},
56660484Sobrien#endif
56760484Sobrien
56860484Sobrien/* Pseudo ops */
56960484Sobrien  {"adr",   0x028f0000, NULL,   NULL,        ARM_ANY,      do_adr},
57060484Sobrien  {"adrl",  0x028f0000, NULL,   NULL,        ARM_ANY,      do_adrl},
57160484Sobrien  {"nop",   0x01a00000, NULL,   NULL,        ARM_ANY,      do_nop},
57260484Sobrien
57360484Sobrien/* ARM 2 multiplies */
57460484Sobrien  {"mul",   0x00000090, NULL,   s_flag,      ARM_2UP,      do_mul},
57560484Sobrien  {"mla",   0x00200090, NULL,   s_flag,      ARM_2UP,      do_mla},
57660484Sobrien
57760484Sobrien/* ARM 3 - swp instructions */
57860484Sobrien  {"swp",   0x01000090, NULL,   byte_flag,   ARM_3UP,      do_swap},
57960484Sobrien
58060484Sobrien/* ARM 6 Coprocessor instructions */
58160484Sobrien  {"mrs",   0x010f0000, NULL,   NULL,        ARM_6UP,      do_mrs},
58260484Sobrien  {"msr",   0x0120f000, NULL,   NULL,        ARM_6UP,      do_msr},
58360484Sobrien/* ScottB: our code uses 0x0128f000 for msr.
58461843Sobrien   NickC:  but this is wrong because the bits 16 through 19 are
58561843Sobrien           handled by the PSR_xxx defines above.  */
58660484Sobrien
58760484Sobrien/* ARM 7M long multiplies - need signed/unsigned flags! */
58860484Sobrien  {"smull", 0x00c00090, NULL,   s_flag,      ARM_LONGMUL,  do_mull},
58960484Sobrien  {"umull", 0x00800090, NULL,   s_flag,      ARM_LONGMUL,  do_mull},
59060484Sobrien  {"smlal", 0x00e00090, NULL,   s_flag,      ARM_LONGMUL,  do_mull},
59160484Sobrien  {"umlal", 0x00a00090, NULL,   s_flag,      ARM_LONGMUL,  do_mull},
59260484Sobrien
59360484Sobrien/* ARM THUMB interworking */
59460484Sobrien  {"bx",    0x012fff10, NULL,   NULL,        ARM_THUMB,    do_bx},
59560484Sobrien
59660484Sobrien/* Floating point instructions */
59760484Sobrien  {"wfs",   0x0e200110, NULL,   NULL,        FPU_ALL,      do_fp_ctrl},
59860484Sobrien  {"rfs",   0x0e300110, NULL,   NULL,        FPU_ALL,      do_fp_ctrl},
59960484Sobrien  {"wfc",   0x0e400110, NULL,   NULL,        FPU_ALL,      do_fp_ctrl},
60060484Sobrien  {"rfc",   0x0e500110, NULL,   NULL,        FPU_ALL,      do_fp_ctrl},
60160484Sobrien  {"ldf",   0x0c100100, "sdep", NULL,        FPU_ALL,      do_fp_ldst},
60260484Sobrien  {"stf",   0x0c000100, "sdep", NULL,        FPU_ALL,      do_fp_ldst},
60360484Sobrien  {"lfm",   0x0c100200, NULL,   lfm_flags,   FPU_MEMMULTI, do_fp_ldmstm},
60460484Sobrien  {"sfm",   0x0c000200, NULL,   sfm_flags,   FPU_MEMMULTI, do_fp_ldmstm},
60560484Sobrien  {"mvf",   0x0e008100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
60660484Sobrien  {"mnf",   0x0e108100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
60760484Sobrien  {"abs",   0x0e208100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
60860484Sobrien  {"rnd",   0x0e308100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
60960484Sobrien  {"sqt",   0x0e408100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
61060484Sobrien  {"log",   0x0e508100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
61160484Sobrien  {"lgn",   0x0e608100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
61260484Sobrien  {"exp",   0x0e708100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
61360484Sobrien  {"sin",   0x0e808100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
61460484Sobrien  {"cos",   0x0e908100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
61560484Sobrien  {"tan",   0x0ea08100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
61660484Sobrien  {"asn",   0x0eb08100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
61760484Sobrien  {"acs",   0x0ec08100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
61860484Sobrien  {"atn",   0x0ed08100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
61960484Sobrien  {"urd",   0x0ee08100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
62060484Sobrien  {"nrm",   0x0ef08100, "sde",  round_flags, FPU_ALL,      do_fp_monadic},
62160484Sobrien  {"adf",   0x0e000100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
62260484Sobrien  {"suf",   0x0e200100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
62360484Sobrien  {"rsf",   0x0e300100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
62460484Sobrien  {"muf",   0x0e100100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
62560484Sobrien  {"dvf",   0x0e400100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
62660484Sobrien  {"rdf",   0x0e500100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
62760484Sobrien  {"pow",   0x0e600100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
62860484Sobrien  {"rpw",   0x0e700100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
62960484Sobrien  {"rmf",   0x0e800100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
63060484Sobrien  {"fml",   0x0e900100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
63160484Sobrien  {"fdv",   0x0ea00100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
63260484Sobrien  {"frd",   0x0eb00100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
63360484Sobrien  {"pol",   0x0ec00100, "sde",  round_flags, FPU_ALL,      do_fp_dyadic},
63460484Sobrien  {"cmf",   0x0e90f110, NULL,   except_flag, FPU_ALL,      do_fp_cmp},
63560484Sobrien  {"cnf",   0x0eb0f110, NULL,   except_flag, FPU_ALL,      do_fp_cmp},
63660484Sobrien/* The FPA10 data sheet suggests that the 'E' of cmfe/cnfe should not
63760484Sobrien   be an optional suffix, but part of the instruction.  To be compatible,
63860484Sobrien   we accept either.  */
63960484Sobrien  {"cmfe",  0x0ed0f110, NULL,   NULL,        FPU_ALL,      do_fp_cmp},
64060484Sobrien  {"cnfe",  0x0ef0f110, NULL,   NULL,        FPU_ALL,      do_fp_cmp},
64160484Sobrien  {"flt",   0x0e000110, "sde",  round_flags, FPU_ALL,      do_fp_from_reg},
64260484Sobrien  {"fix",   0x0e100110, NULL,   fix_flags,   FPU_ALL,      do_fp_to_reg},
64360484Sobrien
64460484Sobrien/* Generic copressor instructions.  */
64560484Sobrien  {"cdp",   0x0e000000, NULL,  NULL,         ARM_2UP,      do_cdp},
64660484Sobrien  {"ldc",   0x0c100000, NULL,  cplong_flag,  ARM_2UP,      do_lstc},
64760484Sobrien  {"stc",   0x0c000000, NULL,  cplong_flag,  ARM_2UP,      do_lstc},
64860484Sobrien  {"mcr",   0x0e000010, NULL,  NULL,         ARM_2UP,      do_co_reg},
64960484Sobrien  {"mrc",   0x0e100010, NULL,  NULL,         ARM_2UP,      do_co_reg},
65060484Sobrien};
65160484Sobrien
65260484Sobrien/* Defines for various bits that we will want to toggle.  */
65360484Sobrien#define INST_IMMEDIATE	0x02000000
65460484Sobrien#define OFFSET_REG	0x02000000
65560484Sobrien#define HWOFFSET_IMM    0x00400000
65660484Sobrien#define SHIFT_BY_REG	0x00000010
65760484Sobrien#define PRE_INDEX	0x01000000
65860484Sobrien#define INDEX_UP	0x00800000
65960484Sobrien#define WRITE_BACK	0x00200000
66060484Sobrien#define LDM_TYPE_2_OR_3	0x00400000
66160484Sobrien
66260484Sobrien#define LITERAL_MASK	0xf000f000
66360484Sobrien#define COND_MASK	0xf0000000
66460484Sobrien#define OPCODE_MASK	0xfe1fffff
66560484Sobrien#define DATA_OP_SHIFT	21
66660484Sobrien
66760484Sobrien/* Codes to distinguish the arithmetic instructions.  */
66860484Sobrien#define OPCODE_AND	0
66960484Sobrien#define OPCODE_EOR	1
67060484Sobrien#define OPCODE_SUB	2
67160484Sobrien#define OPCODE_RSB	3
67260484Sobrien#define OPCODE_ADD	4
67360484Sobrien#define OPCODE_ADC	5
67460484Sobrien#define OPCODE_SBC	6
67560484Sobrien#define OPCODE_RSC	7
67660484Sobrien#define OPCODE_TST	8
67760484Sobrien#define OPCODE_TEQ	9
67860484Sobrien#define OPCODE_CMP	10
67960484Sobrien#define OPCODE_CMN	11
68060484Sobrien#define OPCODE_ORR	12
68160484Sobrien#define OPCODE_MOV	13
68260484Sobrien#define OPCODE_BIC	14
68360484Sobrien#define OPCODE_MVN	15
68460484Sobrien
68560484Sobrienstatic void do_t_nop		PARAMS ((char *));
68660484Sobrienstatic void do_t_arit		PARAMS ((char *));
68760484Sobrienstatic void do_t_add		PARAMS ((char *));
68860484Sobrienstatic void do_t_asr		PARAMS ((char *));
68960484Sobrienstatic void do_t_branch9	PARAMS ((char *));
69060484Sobrienstatic void do_t_branch12	PARAMS ((char *));
69160484Sobrienstatic void do_t_branch23	PARAMS ((char *));
69260484Sobrienstatic void do_t_bx		PARAMS ((char *));
69360484Sobrienstatic void do_t_compare	PARAMS ((char *));
69460484Sobrienstatic void do_t_ldmstm		PARAMS ((char *));
69560484Sobrienstatic void do_t_ldr		PARAMS ((char *));
69660484Sobrienstatic void do_t_ldrb		PARAMS ((char *));
69760484Sobrienstatic void do_t_ldrh		PARAMS ((char *));
69860484Sobrienstatic void do_t_lds		PARAMS ((char *));
69960484Sobrienstatic void do_t_lsl		PARAMS ((char *));
70060484Sobrienstatic void do_t_lsr		PARAMS ((char *));
70160484Sobrienstatic void do_t_mov		PARAMS ((char *));
70260484Sobrienstatic void do_t_push_pop	PARAMS ((char *));
70360484Sobrienstatic void do_t_str		PARAMS ((char *));
70460484Sobrienstatic void do_t_strb		PARAMS ((char *));
70560484Sobrienstatic void do_t_strh		PARAMS ((char *));
70660484Sobrienstatic void do_t_sub		PARAMS ((char *));
70760484Sobrienstatic void do_t_swi		PARAMS ((char *));
70860484Sobrienstatic void do_t_adr		PARAMS ((char *));
70960484Sobrien
71060484Sobrien#define T_OPCODE_MUL 0x4340
71160484Sobrien#define T_OPCODE_TST 0x4200
71260484Sobrien#define T_OPCODE_CMN 0x42c0
71360484Sobrien#define T_OPCODE_NEG 0x4240
71460484Sobrien#define T_OPCODE_MVN 0x43c0
71560484Sobrien
71660484Sobrien#define T_OPCODE_ADD_R3	0x1800
71760484Sobrien#define T_OPCODE_SUB_R3 0x1a00
71860484Sobrien#define T_OPCODE_ADD_HI 0x4400
71960484Sobrien#define T_OPCODE_ADD_ST 0xb000
72060484Sobrien#define T_OPCODE_SUB_ST 0xb080
72160484Sobrien#define T_OPCODE_ADD_SP 0xa800
72260484Sobrien#define T_OPCODE_ADD_PC 0xa000
72360484Sobrien#define T_OPCODE_ADD_I8 0x3000
72460484Sobrien#define T_OPCODE_SUB_I8 0x3800
72560484Sobrien#define T_OPCODE_ADD_I3 0x1c00
72660484Sobrien#define T_OPCODE_SUB_I3 0x1e00
72760484Sobrien
72860484Sobrien#define T_OPCODE_ASR_R	0x4100
72960484Sobrien#define T_OPCODE_LSL_R	0x4080
73060484Sobrien#define T_OPCODE_LSR_R  0x40c0
73160484Sobrien#define T_OPCODE_ASR_I	0x1000
73260484Sobrien#define T_OPCODE_LSL_I	0x0000
73360484Sobrien#define T_OPCODE_LSR_I	0x0800
73460484Sobrien
73560484Sobrien#define T_OPCODE_MOV_I8	0x2000
73660484Sobrien#define T_OPCODE_CMP_I8 0x2800
73760484Sobrien#define T_OPCODE_CMP_LR 0x4280
73860484Sobrien#define T_OPCODE_MOV_HR 0x4600
73960484Sobrien#define T_OPCODE_CMP_HR 0x4500
74060484Sobrien
74160484Sobrien#define T_OPCODE_LDR_PC 0x4800
74260484Sobrien#define T_OPCODE_LDR_SP 0x9800
74360484Sobrien#define T_OPCODE_STR_SP 0x9000
74460484Sobrien#define T_OPCODE_LDR_IW 0x6800
74560484Sobrien#define T_OPCODE_STR_IW 0x6000
74660484Sobrien#define T_OPCODE_LDR_IH 0x8800
74760484Sobrien#define T_OPCODE_STR_IH 0x8000
74860484Sobrien#define T_OPCODE_LDR_IB 0x7800
74960484Sobrien#define T_OPCODE_STR_IB 0x7000
75060484Sobrien#define T_OPCODE_LDR_RW 0x5800
75160484Sobrien#define T_OPCODE_STR_RW 0x5000
75260484Sobrien#define T_OPCODE_LDR_RH 0x5a00
75360484Sobrien#define T_OPCODE_STR_RH 0x5200
75460484Sobrien#define T_OPCODE_LDR_RB 0x5c00
75560484Sobrien#define T_OPCODE_STR_RB 0x5400
75660484Sobrien
75760484Sobrien#define T_OPCODE_PUSH	0xb400
75860484Sobrien#define T_OPCODE_POP	0xbc00
75960484Sobrien
76060484Sobrien#define T_OPCODE_BRANCH 0xe7fe
76160484Sobrien
76260484Sobrienstatic int thumb_reg		PARAMS ((char ** str, int hi_lo));
76360484Sobrien
76460484Sobrien#define THUMB_SIZE	2	/* Size of thumb instruction.  */
76560484Sobrien#define THUMB_REG_LO	0x1
76660484Sobrien#define THUMB_REG_HI	0x2
76760484Sobrien#define THUMB_REG_ANY	0x3
76860484Sobrien
76960484Sobrien#define THUMB_H1	0x0080
77060484Sobrien#define THUMB_H2	0x0040
77160484Sobrien
77260484Sobrien#define THUMB_ASR 0
77360484Sobrien#define THUMB_LSL 1
77460484Sobrien#define THUMB_LSR 2
77560484Sobrien
77660484Sobrien#define THUMB_MOVE 0
77760484Sobrien#define THUMB_COMPARE 1
77860484Sobrien
77960484Sobrien#define THUMB_LOAD 0
78060484Sobrien#define THUMB_STORE 1
78160484Sobrien
78260484Sobrien#define THUMB_PP_PC_LR 0x0100
78360484Sobrien
78460484Sobrien/* These three are used for immediate shifts, do not alter.  */
78560484Sobrien#define THUMB_WORD 2
78660484Sobrien#define THUMB_HALFWORD 1
78760484Sobrien#define THUMB_BYTE 0
78860484Sobrien
78960484Sobrienstruct thumb_opcode
79060484Sobrien{
79160484Sobrien  CONST char *  template;	/* Basic string to match */
79260484Sobrien  unsigned long value;		/* Basic instruction code */
79360484Sobrien  int           size;
79460484Sobrien  unsigned long          variants;    /* Which CPU variants this exists for */
79560484Sobrien  void (*       parms) PARAMS ((char *));  /* Function to call to parse args */
79660484Sobrien};
79760484Sobrien
79860484Sobrienstatic CONST struct thumb_opcode tinsns[] =
79960484Sobrien{
80060484Sobrien  {"adc",	0x4140,		2,	ARM_THUMB, do_t_arit},
80160484Sobrien  {"add",	0x0000,		2,	ARM_THUMB, do_t_add},
80260484Sobrien  {"and",	0x4000,		2,	ARM_THUMB, do_t_arit},
80360484Sobrien  {"asr",	0x0000,		2,	ARM_THUMB, do_t_asr},
80460484Sobrien  {"b",		T_OPCODE_BRANCH, 2,	ARM_THUMB, do_t_branch12},
80560484Sobrien  {"beq",	0xd0fe,		2,	ARM_THUMB, do_t_branch9},
80660484Sobrien  {"bne",	0xd1fe,		2,	ARM_THUMB, do_t_branch9},
80760484Sobrien  {"bcs",	0xd2fe,		2,	ARM_THUMB, do_t_branch9},
80860484Sobrien  {"bhs",	0xd2fe,		2,	ARM_THUMB, do_t_branch9},
80960484Sobrien  {"bcc",	0xd3fe,		2,	ARM_THUMB, do_t_branch9},
81060484Sobrien  {"bul",	0xd3fe,		2,	ARM_THUMB, do_t_branch9},
81160484Sobrien  {"blo",	0xd3fe,		2,	ARM_THUMB, do_t_branch9},
81260484Sobrien  {"bmi",	0xd4fe,		2,	ARM_THUMB, do_t_branch9},
81360484Sobrien  {"bpl",	0xd5fe,		2,	ARM_THUMB, do_t_branch9},
81460484Sobrien  {"bvs",	0xd6fe,		2,	ARM_THUMB, do_t_branch9},
81560484Sobrien  {"bvc",	0xd7fe,		2,	ARM_THUMB, do_t_branch9},
81660484Sobrien  {"bhi",	0xd8fe,		2,	ARM_THUMB, do_t_branch9},
81760484Sobrien  {"bls",	0xd9fe,		2,	ARM_THUMB, do_t_branch9},
81860484Sobrien  {"bge",	0xdafe,		2,	ARM_THUMB, do_t_branch9},
81960484Sobrien  {"blt",	0xdbfe,		2,	ARM_THUMB, do_t_branch9},
82060484Sobrien  {"bgt",	0xdcfe,		2,	ARM_THUMB, do_t_branch9},
82160484Sobrien  {"ble",	0xddfe,		2,	ARM_THUMB, do_t_branch9},
82260484Sobrien  {"bal",	0xdefe,		2,	ARM_THUMB, do_t_branch9},
82360484Sobrien  {"bic",	0x4380,		2,	ARM_THUMB, do_t_arit},
82460484Sobrien  {"bl",	0xf7fffffe,	4,	ARM_THUMB, do_t_branch23},
82560484Sobrien  {"bx",	0x4700,		2,	ARM_THUMB, do_t_bx},
82660484Sobrien  {"cmn",	T_OPCODE_CMN,	2,	ARM_THUMB, do_t_arit},
82760484Sobrien  {"cmp",	0x0000,		2,	ARM_THUMB, do_t_compare},
82860484Sobrien  {"eor",	0x4040,		2,	ARM_THUMB, do_t_arit},
82960484Sobrien  {"ldmia",	0xc800,		2,	ARM_THUMB, do_t_ldmstm},
83060484Sobrien  {"ldr",	0x0000,		2,	ARM_THUMB, do_t_ldr},
83160484Sobrien  {"ldrb",	0x0000,		2,	ARM_THUMB, do_t_ldrb},
83260484Sobrien  {"ldrh",	0x0000,		2,	ARM_THUMB, do_t_ldrh},
83360484Sobrien  {"ldrsb",	0x5600,		2,	ARM_THUMB, do_t_lds},
83460484Sobrien  {"ldrsh",	0x5e00,		2,	ARM_THUMB, do_t_lds},
83560484Sobrien  {"ldsb",	0x5600,		2,	ARM_THUMB, do_t_lds},
83660484Sobrien  {"ldsh",	0x5e00,		2,	ARM_THUMB, do_t_lds},
83760484Sobrien  {"lsl",	0x0000,		2,	ARM_THUMB, do_t_lsl},
83860484Sobrien  {"lsr",	0x0000,		2,	ARM_THUMB, do_t_lsr},
83960484Sobrien  {"mov",	0x0000,		2,	ARM_THUMB, do_t_mov},
84060484Sobrien  {"mul",	T_OPCODE_MUL,	2,	ARM_THUMB, do_t_arit},
84160484Sobrien  {"mvn",	T_OPCODE_MVN,	2,	ARM_THUMB, do_t_arit},
84260484Sobrien  {"neg",	T_OPCODE_NEG,	2,	ARM_THUMB, do_t_arit},
84360484Sobrien  {"orr",	0x4300,		2,	ARM_THUMB, do_t_arit},
84460484Sobrien  {"pop",	0xbc00,		2,	ARM_THUMB, do_t_push_pop},
84560484Sobrien  {"push",	0xb400,		2,	ARM_THUMB, do_t_push_pop},
84660484Sobrien  {"ror",	0x41c0,		2,	ARM_THUMB, do_t_arit},
84760484Sobrien  {"sbc",	0x4180,		2,	ARM_THUMB, do_t_arit},
84860484Sobrien  {"stmia",	0xc000,		2,	ARM_THUMB, do_t_ldmstm},
84960484Sobrien  {"str",	0x0000,		2,	ARM_THUMB, do_t_str},
85060484Sobrien  {"strb",	0x0000,		2,	ARM_THUMB, do_t_strb},
85160484Sobrien  {"strh",	0x0000,		2,	ARM_THUMB, do_t_strh},
85260484Sobrien  {"swi",	0xdf00,		2,	ARM_THUMB, do_t_swi},
85360484Sobrien  {"sub",	0x0000,		2,	ARM_THUMB, do_t_sub},
85460484Sobrien  {"tst",	T_OPCODE_TST,	2,	ARM_THUMB, do_t_arit},
85560484Sobrien  /* Pseudo ops: */
85660484Sobrien  {"adr",       0x0000,         2,      ARM_THUMB, do_t_adr},
85760484Sobrien  {"nop",       0x46C0,         2,      ARM_THUMB, do_t_nop},      /* mov r8,r8 */
85860484Sobrien};
85960484Sobrien
86060484Sobrienstruct reg_entry
86160484Sobrien{
86260484Sobrien  CONST char * name;
86360484Sobrien  int          number;
86460484Sobrien};
86560484Sobrien
86660484Sobrien#define int_register(reg) ((reg) >= 0 && (reg) <= 15)
86760484Sobrien#define cp_register(reg) ((reg) >= 32 && (reg) <= 47)
86860484Sobrien#define fp_register(reg) ((reg) >= 16 && (reg) <= 23)
86960484Sobrien
87060484Sobrien#define REG_PC	15
87160484Sobrien#define REG_LR  14
87260484Sobrien#define REG_SP  13
87360484Sobrien
87460484Sobrien/* These are the standard names.  Users can add aliases with .req  */
87560484Sobrienstatic CONST struct reg_entry reg_table[] =
87660484Sobrien{
87760484Sobrien  /* Processor Register Numbers.  */
87860484Sobrien  {"r0", 0},    {"r1", 1},      {"r2", 2},      {"r3", 3},
87960484Sobrien  {"r4", 4},    {"r5", 5},      {"r6", 6},      {"r7", 7},
88060484Sobrien  {"r8", 8},    {"r9", 9},      {"r10", 10},    {"r11", 11},
88160484Sobrien  {"r12", 12},  {"r13", REG_SP},{"r14", REG_LR},{"r15", REG_PC},
88260484Sobrien  /* APCS conventions.  */
88360484Sobrien  {"a1", 0},	{"a2", 1},    {"a3", 2},     {"a4", 3},
88460484Sobrien  {"v1", 4},	{"v2", 5},    {"v3", 6},     {"v4", 7},     {"v5", 8},
88560484Sobrien  {"v6", 9},	{"sb", 9},    {"v7", 10},    {"sl", 10},
88660484Sobrien  {"fp", 11},	{"ip", 12},   {"sp", REG_SP},{"lr", REG_LR},{"pc", REG_PC},
88760484Sobrien  /* ATPCS additions to APCS conventions.  */
88860484Sobrien  {"wr", 7},    {"v8", 11},
88960484Sobrien  /* FP Registers.  */
89060484Sobrien  {"f0", 16},   {"f1", 17},   {"f2", 18},   {"f3", 19},
89160484Sobrien  {"f4", 20},   {"f5", 21},   {"f6", 22},   {"f7", 23},
89260484Sobrien  {"c0", 32},   {"c1", 33},   {"c2", 34},   {"c3", 35},
89360484Sobrien  {"c4", 36},   {"c5", 37},   {"c6", 38},   {"c7", 39},
89460484Sobrien  {"c8", 40},   {"c9", 41},   {"c10", 42},  {"c11", 43},
89560484Sobrien  {"c12", 44},  {"c13", 45},  {"c14", 46},  {"c15", 47},
89660484Sobrien  {"cr0", 32},  {"cr1", 33},  {"cr2", 34},  {"cr3", 35},
89760484Sobrien  {"cr4", 36},  {"cr5", 37},  {"cr6", 38},  {"cr7", 39},
89860484Sobrien  {"cr8", 40},  {"cr9", 41},  {"cr10", 42}, {"cr11", 43},
89960484Sobrien  {"cr12", 44}, {"cr13", 45}, {"cr14", 46}, {"cr15", 47},
90060484Sobrien  /* ATPCS additions to float register names.  */
90160484Sobrien  {"s0",16},	{"s1",17},	{"s2",18},	{"s3",19},
90260484Sobrien  {"s4",20},	{"s5",21},	{"s6",22},	{"s7",23},
90360484Sobrien  {"d0",16},	{"d1",17},	{"d2",18},	{"d3",19},
90460484Sobrien  {"d4",20},	{"d5",21},	{"d6",22},	{"d7",23},
90560484Sobrien  /* FIXME: At some point we need to add VFP register names.  */
90660484Sobrien  /* Array terminator.  */
90760484Sobrien  {NULL, 0}
90860484Sobrien};
90960484Sobrien
91060484Sobrien#define BAD_ARGS 	_("Bad arguments to instruction")
91160484Sobrien#define BAD_PC 		_("r15 not allowed here")
91260484Sobrien#define BAD_FLAGS 	_("Instruction should not have flags")
91360484Sobrien#define BAD_COND 	_("Instruction is not conditional")
91460484Sobrien
91560484Sobrienstatic struct hash_control * arm_ops_hsh = NULL;
91660484Sobrienstatic struct hash_control * arm_tops_hsh = NULL;
91760484Sobrienstatic struct hash_control * arm_cond_hsh = NULL;
91860484Sobrienstatic struct hash_control * arm_shift_hsh = NULL;
91960484Sobrienstatic struct hash_control * arm_reg_hsh = NULL;
92060484Sobrienstatic struct hash_control * arm_psr_hsh = NULL;
92160484Sobrien
92260484Sobrien/* This table describes all the machine specific pseudo-ops the assembler
92360484Sobrien   has to support.  The fields are:
92460484Sobrien     pseudo-op name without dot
92560484Sobrien     function to call to execute this pseudo-op
92660484Sobrien     Integer arg to pass to the function.  */
92760484Sobrien
92860484Sobrienstatic void s_req PARAMS ((int));
92960484Sobrienstatic void s_align PARAMS ((int));
93060484Sobrienstatic void s_bss PARAMS ((int));
93160484Sobrienstatic void s_even PARAMS ((int));
93260484Sobrienstatic void s_ltorg PARAMS ((int));
93360484Sobrienstatic void s_arm PARAMS ((int));
93460484Sobrienstatic void s_thumb PARAMS ((int));
93560484Sobrienstatic void s_code PARAMS ((int));
93660484Sobrienstatic void s_force_thumb PARAMS ((int));
93760484Sobrienstatic void s_thumb_func PARAMS ((int));
93860484Sobrienstatic void s_thumb_set PARAMS ((int));
93960484Sobrienstatic void arm_s_text PARAMS ((int));
94060484Sobrienstatic void arm_s_data PARAMS ((int));
94160484Sobrien#ifdef OBJ_ELF
94260484Sobrienstatic void arm_s_section PARAMS ((int));
94360484Sobrienstatic void s_arm_elf_cons PARAMS ((int));
94460484Sobrien#endif
94560484Sobrien
94660484Sobrienstatic int my_get_expression PARAMS ((expressionS *, char **));
94760484Sobrien
94860484SobrienCONST pseudo_typeS md_pseudo_table[] =
94960484Sobrien{
95060484Sobrien  { "req",         s_req,         0 },	/* Never called becasue '.req' does not start line */
95160484Sobrien  { "bss",         s_bss,         0 },
95260484Sobrien  { "align",       s_align,       0 },
95360484Sobrien  { "arm",         s_arm,         0 },
95460484Sobrien  { "thumb",       s_thumb,       0 },
95560484Sobrien  { "code",        s_code,        0 },
95660484Sobrien  { "force_thumb", s_force_thumb, 0 },
95760484Sobrien  { "thumb_func",  s_thumb_func,  0 },
95860484Sobrien  { "thumb_set",   s_thumb_set,   0 },
95960484Sobrien  { "even",        s_even,        0 },
96060484Sobrien  { "ltorg",       s_ltorg,       0 },
96160484Sobrien  { "pool",        s_ltorg,       0 },
96260484Sobrien  /* Allow for the effect of section changes.  */
96360484Sobrien  { "text",        arm_s_text,    0 },
96460484Sobrien  { "data",        arm_s_data,    0 },
96560484Sobrien#ifdef OBJ_ELF
96660484Sobrien  { "section",     arm_s_section, 0 },
96760484Sobrien  { "section.s",   arm_s_section, 0 },
96860484Sobrien  { "sect",        arm_s_section, 0 },
96960484Sobrien  { "sect.s",      arm_s_section, 0 },
97060484Sobrien  { "word",        s_arm_elf_cons, 4 },
97160484Sobrien  { "long",        s_arm_elf_cons, 4 },
97260484Sobrien#else
97360484Sobrien  { "word",        cons, 4},
97460484Sobrien#endif
97560484Sobrien  { "extend",      float_cons, 'x' },
97660484Sobrien  { "ldouble",     float_cons, 'x' },
97760484Sobrien  { "packed",      float_cons, 'p' },
97860484Sobrien  { 0, 0, 0 }
97960484Sobrien};
98060484Sobrien
98160484Sobrien/* Stuff needed to resolve the label ambiguity
98260484Sobrien   As:
98360484Sobrien     ...
98460484Sobrien     label:   <insn>
98560484Sobrien   may differ from:
98660484Sobrien     ...
98760484Sobrien     label:
98860484Sobrien              <insn>
98960484Sobrien*/
99060484Sobrien
99160484SobriensymbolS *  last_label_seen;
99260484Sobrienstatic int label_is_thumb_function_name = false;
99360484Sobrien
99460484Sobrien/* Literal stuff */
99560484Sobrien
99660484Sobrien#define MAX_LITERAL_POOL_SIZE 1024
99760484Sobrien
99860484Sobrientypedef struct literalS
99960484Sobrien{
100060484Sobrien  struct expressionS  exp;
100160484Sobrien  struct arm_it *     inst;
100260484Sobrien} literalT;
100360484Sobrien
100460484SobrienliteralT  literals[MAX_LITERAL_POOL_SIZE];
100560484Sobrienint       next_literal_pool_place = 0; /* Next free entry in the pool */
100660484Sobrienint       lit_pool_num = 1; /* Next literal pool number */
100760484SobriensymbolS * current_poolP = NULL;
100860484Sobrien
100960484Sobrienstatic int
101060484Sobrienadd_to_lit_pool ()
101160484Sobrien{
101260484Sobrien  int lit_count = 0;
101360484Sobrien
101460484Sobrien  if (current_poolP == NULL)
101560484Sobrien    current_poolP = symbol_create (FAKE_LABEL_NAME, undefined_section,
101660484Sobrien				   (valueT) 0, &zero_address_frag);
101760484Sobrien
101860484Sobrien  /* Check if this literal value is already in the pool:  */
101960484Sobrien  while (lit_count < next_literal_pool_place)
102060484Sobrien    {
102160484Sobrien      if (literals[lit_count].exp.X_op == inst.reloc.exp.X_op
102260484Sobrien          && inst.reloc.exp.X_op == O_constant
102360484Sobrien          && literals[lit_count].exp.X_add_number
102460484Sobrien	     == inst.reloc.exp.X_add_number
102560484Sobrien          && literals[lit_count].exp.X_unsigned == inst.reloc.exp.X_unsigned)
102660484Sobrien        break;
102760484Sobrien      lit_count++;
102860484Sobrien    }
102960484Sobrien
103060484Sobrien  if (lit_count == next_literal_pool_place) /* new entry */
103160484Sobrien    {
103260484Sobrien      if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE)
103360484Sobrien        {
103460484Sobrien          inst.error = _("Literal Pool Overflow");
103560484Sobrien          return FAIL;
103660484Sobrien        }
103760484Sobrien
103860484Sobrien      literals[next_literal_pool_place].exp = inst.reloc.exp;
103960484Sobrien      lit_count = next_literal_pool_place++;
104060484Sobrien    }
104160484Sobrien
104260484Sobrien  inst.reloc.exp.X_op = O_symbol;
104360484Sobrien  inst.reloc.exp.X_add_number = (lit_count) * 4 - 8;
104460484Sobrien  inst.reloc.exp.X_add_symbol = current_poolP;
104560484Sobrien
104660484Sobrien  return SUCCESS;
104760484Sobrien}
104860484Sobrien
104960484Sobrien/* Can't use symbol_new here, so have to create a symbol and then at
105060484Sobrien   a later date assign it a value. Thats what these functions do.  */
105160484Sobrienstatic void
105260484Sobriensymbol_locate (symbolP, name, segment, valu, frag)
105360484Sobrien     symbolS *    symbolP;
105460484Sobrien     CONST char * name;		/* It is copied, the caller can modify */
105560484Sobrien     segT         segment;	/* Segment identifier (SEG_<something>) */
105660484Sobrien     valueT       valu;		/* Symbol value */
105760484Sobrien     fragS *      frag;		/* Associated fragment */
105860484Sobrien{
105960484Sobrien  unsigned int name_length;
106060484Sobrien  char * preserved_copy_of_name;
106160484Sobrien
106260484Sobrien  name_length = strlen (name) + 1;      /* +1 for \0 */
106360484Sobrien  obstack_grow (&notes, name, name_length);
106460484Sobrien  preserved_copy_of_name = obstack_finish (&notes);
106560484Sobrien#ifdef STRIP_UNDERSCORE
106660484Sobrien  if (preserved_copy_of_name[0] == '_')
106760484Sobrien    preserved_copy_of_name++;
106860484Sobrien#endif
106960484Sobrien
107060484Sobrien#ifdef tc_canonicalize_symbol_name
107160484Sobrien  preserved_copy_of_name =
107260484Sobrien    tc_canonicalize_symbol_name (preserved_copy_of_name);
107360484Sobrien#endif
107460484Sobrien
107560484Sobrien  S_SET_NAME (symbolP, preserved_copy_of_name);
107660484Sobrien
107760484Sobrien  S_SET_SEGMENT (symbolP, segment);
107860484Sobrien  S_SET_VALUE (symbolP, valu);
107960484Sobrien  symbol_clear_list_pointers(symbolP);
108060484Sobrien
108160484Sobrien  symbol_set_frag (symbolP, frag);
108260484Sobrien
108360484Sobrien  /* Link to end of symbol chain.  */
108460484Sobrien  {
108560484Sobrien    extern int symbol_table_frozen;
108660484Sobrien    if (symbol_table_frozen)
108760484Sobrien      abort ();
108860484Sobrien  }
108960484Sobrien
109060484Sobrien  symbol_append (symbolP, symbol_lastP, & symbol_rootP, & symbol_lastP);
109160484Sobrien
109260484Sobrien  obj_symbol_new_hook (symbolP);
109360484Sobrien
109460484Sobrien#ifdef tc_symbol_new_hook
109560484Sobrien  tc_symbol_new_hook (symbolP);
109660484Sobrien#endif
109760484Sobrien
109860484Sobrien#ifdef DEBUG_SYMS
109960484Sobrien  verify_symbol_chain (symbol_rootP, symbol_lastP);
110060484Sobrien#endif /* DEBUG_SYMS */
110160484Sobrien}
110260484Sobrien
110360484Sobrien/* Check that an immediate is valid, and if so,
110460484Sobrien   convert it to the right format.  */
110560484Sobrienstatic unsigned int
110660484Sobrienvalidate_immediate (val)
110760484Sobrien     unsigned int val;
110860484Sobrien{
110960484Sobrien  unsigned int a;
111060484Sobrien  unsigned int i;
111160484Sobrien
111260484Sobrien#define rotate_left(v, n) (v << n | v >> (32 - n))
111360484Sobrien
111460484Sobrien  for (i = 0; i < 32; i += 2)
111560484Sobrien    if ((a = rotate_left (val, i)) <= 0xff)
111660484Sobrien      return a | (i << 7); /* 12-bit pack: [shift-cnt,const] */
111760484Sobrien
111860484Sobrien  return FAIL;
111960484Sobrien}
112060484Sobrien
112160484Sobrien/* Check to see if an immediate can be computed as two seperate immediate
112260484Sobrien   values, added together.  We already know that this value cannot be
112360484Sobrien   computed by just one ARM instruction.  */
112460484Sobrienstatic unsigned int
112560484Sobrienvalidate_immediate_twopart (val, highpart)
112660484Sobrien     unsigned int val;
112760484Sobrien     unsigned int * highpart;
112860484Sobrien{
112960484Sobrien  unsigned int a;
113060484Sobrien  unsigned int i;
113160484Sobrien
113260484Sobrien  for (i = 0; i < 32; i += 2)
113360484Sobrien    if (((a = rotate_left (val, i)) & 0xff) != 0)
113460484Sobrien      {
113560484Sobrien	if (a & 0xff00)
113660484Sobrien	  {
113760484Sobrien	    if (a & ~ 0xffff)
113860484Sobrien	      continue;
113960484Sobrien	    * highpart = (a  >> 8) | ((i + 24) << 7);
114060484Sobrien	  }
114160484Sobrien	else if (a & 0xff0000)
114260484Sobrien	  {
114360484Sobrien	    if (a & 0xff000000)
114460484Sobrien	      continue;
114560484Sobrien
114660484Sobrien	    * highpart = (a >> 16) | ((i + 16) << 7);
114760484Sobrien	  }
114860484Sobrien	else
114960484Sobrien	  {
115060484Sobrien	    assert (a & 0xff000000);
115160484Sobrien
115260484Sobrien	    * highpart = (a >> 24) | ((i + 8) << 7);
115360484Sobrien	  }
115460484Sobrien
115560484Sobrien	return (a & 0xff) | (i << 7);
115660484Sobrien      }
115760484Sobrien
115860484Sobrien  return FAIL;
115960484Sobrien}
116060484Sobrien
116160484Sobrienstatic int
116260484Sobrienvalidate_offset_imm (val, hwse)
116360484Sobrien     unsigned int val;
116460484Sobrien     int hwse;
116560484Sobrien{
116660484Sobrien  if ((hwse && val > 255) || val > 4095)
116760484Sobrien     return FAIL;
116860484Sobrien  return val;
116960484Sobrien}
117060484Sobrien
117160484Sobrien
117260484Sobrienstatic void
117360484Sobriens_req (a)
117460484Sobrien     int a ATTRIBUTE_UNUSED;
117560484Sobrien{
117660484Sobrien  as_bad (_("Invalid syntax for .req directive."));
117760484Sobrien}
117860484Sobrien
117960484Sobrienstatic void
118060484Sobriens_bss (ignore)
118160484Sobrien     int ignore ATTRIBUTE_UNUSED;
118260484Sobrien{
118360484Sobrien  /* We don't support putting frags in the BSS segment, we fake it by
118460484Sobrien     marking in_bss, then looking at s_skip for clues?.. */
118560484Sobrien  subseg_set (bss_section, 0);
118660484Sobrien  demand_empty_rest_of_line ();
118760484Sobrien}
118860484Sobrien
118960484Sobrienstatic void
119060484Sobriens_even (ignore)
119160484Sobrien     int ignore ATTRIBUTE_UNUSED;
119260484Sobrien{
119360484Sobrien  if (!need_pass_2)		/* Never make frag if expect extra pass. */
119460484Sobrien    frag_align (1, 0, 0);
119560484Sobrien
119660484Sobrien  record_alignment (now_seg, 1);
119760484Sobrien
119860484Sobrien  demand_empty_rest_of_line ();
119960484Sobrien}
120060484Sobrien
120160484Sobrienstatic void
120260484Sobriens_ltorg (ignored)
120360484Sobrien     int ignored ATTRIBUTE_UNUSED;
120460484Sobrien{
120560484Sobrien  int lit_count = 0;
120660484Sobrien  char sym_name[20];
120760484Sobrien
120860484Sobrien  if (current_poolP == NULL)
120960484Sobrien    return;
121060484Sobrien
121160484Sobrien  /* Align pool as you have word accesses */
121260484Sobrien  /* Only make a frag if we have to ... */
121360484Sobrien  if (!need_pass_2)
121460484Sobrien    frag_align (2, 0, 0);
121560484Sobrien
121660484Sobrien  record_alignment (now_seg, 2);
121760484Sobrien
121860484Sobrien  sprintf (sym_name, "$$lit_\002%x", lit_pool_num++);
121960484Sobrien
122060484Sobrien  symbol_locate (current_poolP, sym_name, now_seg,
122160484Sobrien		 (valueT) frag_now_fix (), frag_now);
122260484Sobrien  symbol_table_insert (current_poolP);
122360484Sobrien
122460484Sobrien  ARM_SET_THUMB (current_poolP, thumb_mode);
122560484Sobrien
122660484Sobrien#if defined OBJ_COFF || defined OBJ_ELF
122760484Sobrien  ARM_SET_INTERWORK (current_poolP, support_interwork);
122860484Sobrien#endif
122960484Sobrien
123060484Sobrien  while (lit_count < next_literal_pool_place)
123160484Sobrien    /* First output the expression in the instruction to the pool.  */
123260484Sobrien    emit_expr (&(literals[lit_count++].exp), 4); /* .word */
123360484Sobrien
123460484Sobrien  next_literal_pool_place = 0;
123560484Sobrien  current_poolP = NULL;
123660484Sobrien}
123760484Sobrien
123860484Sobrienstatic void
123960484Sobriens_align (unused)	/* Same as s_align_ptwo but align 0 => align 2 */
124060484Sobrien     int unused ATTRIBUTE_UNUSED;
124160484Sobrien{
124260484Sobrien  register int temp;
124360484Sobrien  register long temp_fill;
124460484Sobrien  long max_alignment = 15;
124560484Sobrien
124660484Sobrien  temp = get_absolute_expression ();
124760484Sobrien  if (temp > max_alignment)
124860484Sobrien    as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment);
124960484Sobrien  else if (temp < 0)
125060484Sobrien    {
125160484Sobrien      as_bad (_("Alignment negative. 0 assumed."));
125260484Sobrien      temp = 0;
125360484Sobrien    }
125460484Sobrien
125560484Sobrien  if (*input_line_pointer == ',')
125660484Sobrien    {
125760484Sobrien      input_line_pointer++;
125860484Sobrien      temp_fill = get_absolute_expression ();
125960484Sobrien    }
126060484Sobrien  else
126160484Sobrien    temp_fill = 0;
126260484Sobrien
126360484Sobrien  if (!temp)
126460484Sobrien    temp = 2;
126560484Sobrien
126660484Sobrien  /* Only make a frag if we HAVE to. . . */
126760484Sobrien  if (temp && !need_pass_2)
126860484Sobrien    frag_align (temp, (int) temp_fill, 0);
126960484Sobrien  demand_empty_rest_of_line ();
127060484Sobrien
127160484Sobrien  record_alignment (now_seg, temp);
127260484Sobrien}
127360484Sobrien
127460484Sobrienstatic void
127560484Sobriens_force_thumb (ignore)
127660484Sobrien     int ignore ATTRIBUTE_UNUSED;
127760484Sobrien{
127860484Sobrien  /* If we are not already in thumb mode go into it, EVEN if
127960484Sobrien     the target processor does not support thumb instructions.
128060484Sobrien     This is used by gcc/config/arm/lib1funcs.asm for example
128160484Sobrien     to compile interworking support functions even if the
128260484Sobrien     target processor should not support interworking.  */
128360484Sobrien
128460484Sobrien  if (! thumb_mode)
128560484Sobrien    {
128660484Sobrien      thumb_mode = 2;
128760484Sobrien
128860484Sobrien      record_alignment (now_seg, 1);
128960484Sobrien    }
129060484Sobrien
129160484Sobrien  demand_empty_rest_of_line ();
129260484Sobrien}
129360484Sobrien
129460484Sobrienstatic void
129560484Sobriens_thumb_func (ignore)
129660484Sobrien     int ignore ATTRIBUTE_UNUSED;
129760484Sobrien{
129860484Sobrien  /* The following label is the name/address of the start of a Thumb function.
129960484Sobrien     We need to know this for the interworking support.  */
130060484Sobrien
130160484Sobrien  label_is_thumb_function_name = true;
130260484Sobrien
130360484Sobrien  demand_empty_rest_of_line ();
130460484Sobrien}
130560484Sobrien
130660484Sobrien/* Perform a .set directive, but also mark the alias as
130760484Sobrien   being a thumb function.  */
130860484Sobrien
130960484Sobrienstatic void
131060484Sobriens_thumb_set (equiv)
131160484Sobrien     int equiv;
131260484Sobrien{
131360484Sobrien  /* XXX the following is a duplicate of the code for s_set() in read.c
131460484Sobrien     We cannot just call that code as we need to get at the symbol that
131560484Sobrien     is created.  */
131660484Sobrien  register char *    name;
131760484Sobrien  register char      delim;
131860484Sobrien  register char *    end_name;
131960484Sobrien  register symbolS * symbolP;
132060484Sobrien
132160484Sobrien  /*
132260484Sobrien   * Especial apologies for the random logic:
132360484Sobrien   * this just grew, and could be parsed much more simply!
132460484Sobrien   * Dean in haste.
132560484Sobrien   */
132660484Sobrien  name      = input_line_pointer;
132760484Sobrien  delim     = get_symbol_end ();
132860484Sobrien  end_name  = input_line_pointer;
132960484Sobrien  *end_name = delim;
133060484Sobrien
133160484Sobrien  SKIP_WHITESPACE ();
133260484Sobrien
133360484Sobrien  if (*input_line_pointer != ',')
133460484Sobrien    {
133560484Sobrien      *end_name = 0;
133660484Sobrien      as_bad (_("Expected comma after name \"%s\""), name);
133760484Sobrien      *end_name = delim;
133860484Sobrien      ignore_rest_of_line ();
133960484Sobrien      return;
134060484Sobrien    }
134160484Sobrien
134260484Sobrien  input_line_pointer++;
134360484Sobrien  *end_name = 0;
134460484Sobrien
134560484Sobrien  if (name[0] == '.' && name[1] == '\0')
134660484Sobrien    {
134760484Sobrien      /* XXX - this should not happen to .thumb_set  */
134860484Sobrien      abort ();
134960484Sobrien    }
135060484Sobrien
135160484Sobrien  if ((symbolP = symbol_find (name)) == NULL
135260484Sobrien      && (symbolP = md_undefined_symbol (name)) == NULL)
135360484Sobrien    {
135460484Sobrien#ifndef NO_LISTING
135560484Sobrien      /* When doing symbol listings, play games with dummy fragments living
135660484Sobrien	 outside the normal fragment chain to record the file and line info
135760484Sobrien         for this symbol.  */
135860484Sobrien      if (listing & LISTING_SYMBOLS)
135960484Sobrien	{
136060484Sobrien	  extern struct list_info_struct * listing_tail;
136160484Sobrien	  fragS * dummy_frag = (fragS *) xmalloc (sizeof(fragS));
136260484Sobrien	  memset (dummy_frag, 0, sizeof(fragS));
136360484Sobrien	  dummy_frag->fr_type = rs_fill;
136460484Sobrien	  dummy_frag->line = listing_tail;
136560484Sobrien	  symbolP = symbol_new (name, undefined_section, 0, dummy_frag);
136660484Sobrien	  dummy_frag->fr_symbol = symbolP;
136760484Sobrien	}
136860484Sobrien      else
136960484Sobrien#endif
137060484Sobrien        symbolP = symbol_new (name, undefined_section, 0, &zero_address_frag);
137160484Sobrien
137260484Sobrien#ifdef OBJ_COFF
137360484Sobrien      /* "set" symbols are local unless otherwise specified. */
137460484Sobrien      SF_SET_LOCAL (symbolP);
137560484Sobrien#endif /* OBJ_COFF */
137660484Sobrien    }				/* make a new symbol */
137760484Sobrien
137860484Sobrien  symbol_table_insert (symbolP);
137960484Sobrien
138060484Sobrien  * end_name = delim;
138160484Sobrien
138260484Sobrien  if (equiv
138360484Sobrien      && S_IS_DEFINED (symbolP)
138460484Sobrien      && S_GET_SEGMENT (symbolP) != reg_section)
138560484Sobrien    as_bad (_("symbol `%s' already defined"), S_GET_NAME (symbolP));
138660484Sobrien
138760484Sobrien  pseudo_set (symbolP);
138860484Sobrien
138960484Sobrien  demand_empty_rest_of_line ();
139060484Sobrien
139160484Sobrien  /* XXX Now we come to the Thumb specific bit of code.  */
139260484Sobrien
139360484Sobrien  THUMB_SET_FUNC (symbolP, 1);
139460484Sobrien  ARM_SET_THUMB (symbolP, 1);
139560484Sobrien#if defined OBJ_ELF || defined OBJ_COFF
139660484Sobrien  ARM_SET_INTERWORK (symbolP, support_interwork);
139760484Sobrien#endif
139860484Sobrien}
139960484Sobrien
140060484Sobrien/* If we change section we must dump the literal pool first.  */
140160484Sobrienstatic void
140260484Sobrienarm_s_text (ignore)
140360484Sobrien     int ignore;
140460484Sobrien{
140560484Sobrien  if (now_seg != text_section)
140660484Sobrien    s_ltorg (0);
140760484Sobrien
140860484Sobrien#ifdef OBJ_ELF
140960484Sobrien  obj_elf_text (ignore);
141060484Sobrien#else
141160484Sobrien  s_text (ignore);
141260484Sobrien#endif
141360484Sobrien}
141460484Sobrien
141560484Sobrienstatic void
141660484Sobrienarm_s_data (ignore)
141760484Sobrien     int ignore;
141860484Sobrien{
141960484Sobrien  if (flag_readonly_data_in_text)
142060484Sobrien    {
142160484Sobrien      if (now_seg != text_section)
142260484Sobrien	s_ltorg (0);
142360484Sobrien    }
142460484Sobrien  else if (now_seg != data_section)
142560484Sobrien    s_ltorg (0);
142660484Sobrien
142760484Sobrien#ifdef OBJ_ELF
142860484Sobrien  obj_elf_data (ignore);
142960484Sobrien#else
143060484Sobrien  s_data (ignore);
143160484Sobrien#endif
143260484Sobrien}
143360484Sobrien
143460484Sobrien#ifdef OBJ_ELF
143560484Sobrienstatic void
143660484Sobrienarm_s_section (ignore)
143760484Sobrien     int ignore;
143860484Sobrien{
143960484Sobrien  s_ltorg (0);
144060484Sobrien
144160484Sobrien  obj_elf_section (ignore);
144260484Sobrien}
144360484Sobrien#endif
144460484Sobrien
144560484Sobrienstatic void
144660484Sobrienopcode_select (width)
144760484Sobrien     int width;
144860484Sobrien{
144960484Sobrien  switch (width)
145060484Sobrien    {
145160484Sobrien    case 16:
145260484Sobrien      if (! thumb_mode)
145360484Sobrien	{
145460484Sobrien	  if (! (cpu_variant & ARM_THUMB))
145560484Sobrien	    as_bad (_("selected processor does not support THUMB opcodes"));
145660484Sobrien	  thumb_mode = 1;
145760484Sobrien          /* No need to force the alignment, since we will have been
145860484Sobrien             coming from ARM mode, which is word-aligned. */
145960484Sobrien          record_alignment (now_seg, 1);
146060484Sobrien	}
146160484Sobrien      break;
146260484Sobrien
146360484Sobrien    case 32:
146460484Sobrien      if (thumb_mode)
146560484Sobrien	{
146660484Sobrien          if ((cpu_variant & ARM_ANY) == ARM_THUMB)
146760484Sobrien	    as_bad (_("selected processor does not support ARM opcodes"));
146860484Sobrien	  thumb_mode = 0;
146960484Sobrien          if (!need_pass_2)
147060484Sobrien            frag_align (2, 0, 0);
147160484Sobrien          record_alignment (now_seg, 1);
147260484Sobrien	}
147360484Sobrien      break;
147460484Sobrien
147560484Sobrien    default:
147660484Sobrien      as_bad (_("invalid instruction size selected (%d)"), width);
147760484Sobrien    }
147860484Sobrien}
147960484Sobrien
148060484Sobrienstatic void
148160484Sobriens_arm (ignore)
148260484Sobrien     int ignore ATTRIBUTE_UNUSED;
148360484Sobrien{
148460484Sobrien  opcode_select (32);
148560484Sobrien  demand_empty_rest_of_line ();
148660484Sobrien}
148760484Sobrien
148860484Sobrienstatic void
148960484Sobriens_thumb (ignore)
149060484Sobrien     int ignore ATTRIBUTE_UNUSED;
149160484Sobrien{
149260484Sobrien  opcode_select (16);
149360484Sobrien  demand_empty_rest_of_line ();
149460484Sobrien}
149560484Sobrien
149660484Sobrienstatic void
149760484Sobriens_code (unused)
149860484Sobrien     int unused ATTRIBUTE_UNUSED;
149960484Sobrien{
150060484Sobrien  register int temp;
150160484Sobrien
150260484Sobrien  temp = get_absolute_expression ();
150360484Sobrien  switch (temp)
150460484Sobrien    {
150560484Sobrien    case 16:
150660484Sobrien    case 32:
150760484Sobrien      opcode_select (temp);
150860484Sobrien      break;
150960484Sobrien
151060484Sobrien    default:
151160484Sobrien      as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp);
151260484Sobrien    }
151360484Sobrien}
151460484Sobrien
151560484Sobrienstatic void
151660484Sobrienend_of_line (str)
151760484Sobrien     char * str;
151860484Sobrien{
151960484Sobrien  skip_whitespace (str);
152060484Sobrien
152160484Sobrien  if (* str != '\0')
152260484Sobrien    inst.error = _("Garbage following instruction");
152360484Sobrien}
152460484Sobrien
152560484Sobrienstatic int
152660484Sobrienskip_past_comma (str)
152760484Sobrien     char ** str;
152860484Sobrien{
152960484Sobrien  char *p = *str, c;
153060484Sobrien  int comma = 0;
153160484Sobrien
153260484Sobrien  while ((c = *p) == ' ' || c == ',')
153360484Sobrien    {
153460484Sobrien      p++;
153560484Sobrien      if (c == ',' && comma++)
153660484Sobrien	return FAIL;
153760484Sobrien    }
153860484Sobrien
153960484Sobrien  if (c == '\0')
154060484Sobrien    return FAIL;
154160484Sobrien
154260484Sobrien  *str = p;
154360484Sobrien  return comma ? SUCCESS : FAIL;
154460484Sobrien}
154560484Sobrien
154660484Sobrien/* A standard register must be given at this point.
154760484Sobrien   Shift is the place to put it in inst.instruction.
154860484Sobrien   Restores input start point on err.
154960484Sobrien   Returns the reg#, or FAIL.  */
155060484Sobrienstatic int
155160484Sobrienreg_required_here (str, shift)
155260484Sobrien     char ** str;
155360484Sobrien     int     shift;
155460484Sobrien{
155560484Sobrien  static char buff [128]; /* XXX */
155660484Sobrien  int    reg;
155760484Sobrien  char * start = *str;
155860484Sobrien
155960484Sobrien  if ((reg = arm_reg_parse (str)) != FAIL && int_register (reg))
156060484Sobrien    {
156160484Sobrien      if (shift >= 0)
156260484Sobrien	inst.instruction |= reg << shift;
156360484Sobrien      return reg;
156460484Sobrien    }
156560484Sobrien
156660484Sobrien  /* Restore the start point, we may have got a reg of the wrong class.  */
156760484Sobrien  *str = start;
156860484Sobrien
156960484Sobrien  /* In the few cases where we might be able to accept something else
157060484Sobrien     this error can be overridden.  */
157160484Sobrien  sprintf (buff, _("Register expected, not '%.100s'"), start);
157260484Sobrien  inst.error = buff;
157360484Sobrien
157460484Sobrien  return FAIL;
157560484Sobrien}
157660484Sobrien
157761843Sobrienstatic CONST struct asm_psr *
157861843Sobrienarm_psr_parse (ccp)
157961843Sobrien     register char ** ccp;
158061843Sobrien{
158161843Sobrien  char * start = * ccp;
158261843Sobrien  char   c;
158361843Sobrien  char * p;
158461843Sobrien  CONST struct asm_psr * psr;
158561843Sobrien
158661843Sobrien  p = start;
158761843Sobrien
158861843Sobrien  /* Skip to the end of the next word in the input stream.  */
158961843Sobrien  do
159061843Sobrien    {
159161843Sobrien      c = *p++;
159261843Sobrien    }
159361843Sobrien  while (isalpha (c) || c == '_');
159461843Sobrien
159561843Sobrien  /* Terminate the word.  */
159661843Sobrien  *--p = 0;
159761843Sobrien
159861843Sobrien  /* Now locate the word in the psr hash table.  */
159961843Sobrien  psr = (CONST struct asm_psr *) hash_find (arm_psr_hsh, start);
160061843Sobrien
160161843Sobrien  /* Restore the input stream.  */
160261843Sobrien  *p = c;
160361843Sobrien
160461843Sobrien  /* If we found a valid match, advance the
160561843Sobrien     stream pointer past the end of the word.  */
160661843Sobrien  *ccp = p;
160761843Sobrien
160861843Sobrien  return psr;
160961843Sobrien}
161061843Sobrien
161161843Sobrien/* Parse the input looking for a PSR flag.  */
161260484Sobrienstatic int
161361843Sobrienpsr_required_here (str)
161460484Sobrien     char ** str;
161560484Sobrien{
161660484Sobrien  char * start = *str;
161761843Sobrien  CONST struct asm_psr * psr;
161861843Sobrien
161960484Sobrien  psr = arm_psr_parse (str);
162061843Sobrien
162161843Sobrien  if (psr)
162260484Sobrien    {
162361843Sobrien      /* If this is the SPSR that is being modified, set the R bit.  */
162461843Sobrien      if (! psr->cpsr)
162561843Sobrien	inst.instruction |= SPSR_BIT;
162661843Sobrien
162761843Sobrien      /* Set the psr flags in the MSR instruction.  */
162861843Sobrien      inst.instruction |= psr->field << PSR_SHIFT;
162960484Sobrien
163060484Sobrien      return SUCCESS;
163160484Sobrien    }
163260484Sobrien
163361843Sobrien  /* In the few cases where we might be able to accept
163461843Sobrien     something else this error can be overridden.  */
163561843Sobrien  inst.error = _("flag for {c}psr instruction expected");
163660484Sobrien
163760484Sobrien  /* Restore the start point.  */
163860484Sobrien  *str = start;
163960484Sobrien  return FAIL;
164060484Sobrien}
164160484Sobrien
164260484Sobrienstatic int
164360484Sobrienco_proc_number (str)
164460484Sobrien     char ** str;
164560484Sobrien{
164660484Sobrien  int processor, pchar;
164760484Sobrien
164860484Sobrien  skip_whitespace (* str);
164960484Sobrien
165060484Sobrien  /* The data sheet seems to imply that just a number on its own is valid
165160484Sobrien     here, but the RISC iX assembler seems to accept a prefix 'p'.  We will
165260484Sobrien     accept either.  */
165360484Sobrien  if (**str == 'p' || **str == 'P')
165460484Sobrien    (*str)++;
165560484Sobrien
165660484Sobrien  pchar = *(*str)++;
165760484Sobrien  if (pchar >= '0' && pchar <= '9')
165860484Sobrien    {
165960484Sobrien      processor = pchar - '0';
166060484Sobrien      if (**str >= '0' && **str <= '9')
166160484Sobrien	{
166260484Sobrien	  processor = processor * 10 + *(*str)++ - '0';
166360484Sobrien	  if (processor > 15)
166460484Sobrien	    {
166560484Sobrien	      inst.error = _("Illegal co-processor number");
166660484Sobrien	      return FAIL;
166760484Sobrien	    }
166860484Sobrien	}
166960484Sobrien    }
167060484Sobrien  else
167160484Sobrien    {
167260484Sobrien      inst.error = _("Bad or missing co-processor number");
167360484Sobrien      return FAIL;
167460484Sobrien    }
167560484Sobrien
167660484Sobrien  inst.instruction |= processor << 8;
167760484Sobrien  return SUCCESS;
167860484Sobrien}
167960484Sobrien
168060484Sobrienstatic int
168160484Sobriencp_opc_expr (str, where, length)
168260484Sobrien     char ** str;
168360484Sobrien     int where;
168460484Sobrien     int length;
168560484Sobrien{
168660484Sobrien  expressionS expr;
168760484Sobrien
168860484Sobrien  skip_whitespace (* str);
168960484Sobrien
169060484Sobrien  memset (&expr, '\0', sizeof (expr));
169160484Sobrien
169260484Sobrien  if (my_get_expression (&expr, str))
169360484Sobrien    return FAIL;
169460484Sobrien  if (expr.X_op != O_constant)
169560484Sobrien    {
169660484Sobrien      inst.error = _("bad or missing expression");
169760484Sobrien      return FAIL;
169860484Sobrien    }
169960484Sobrien
170060484Sobrien  if ((expr.X_add_number & ((1 << length) - 1)) != expr.X_add_number)
170160484Sobrien    {
170260484Sobrien      inst.error = _("immediate co-processor expression too large");
170360484Sobrien      return FAIL;
170460484Sobrien    }
170560484Sobrien
170660484Sobrien  inst.instruction |= expr.X_add_number << where;
170760484Sobrien  return SUCCESS;
170860484Sobrien}
170960484Sobrien
171060484Sobrienstatic int
171160484Sobriencp_reg_required_here (str, where)
171260484Sobrien     char ** str;
171360484Sobrien     int     where;
171460484Sobrien{
171560484Sobrien  int    reg;
171660484Sobrien  char * start = *str;
171760484Sobrien
171860484Sobrien  if ((reg = arm_reg_parse (str)) != FAIL && cp_register (reg))
171960484Sobrien    {
172060484Sobrien      reg &= 15;
172160484Sobrien      inst.instruction |= reg << where;
172260484Sobrien      return reg;
172360484Sobrien    }
172460484Sobrien
172560484Sobrien  /* In the few cases where we might be able to accept something else
172660484Sobrien     this error can be overridden.  */
172760484Sobrien  inst.error = _("Co-processor register expected");
172860484Sobrien
172960484Sobrien  /* Restore the start point.  */
173060484Sobrien  *str = start;
173160484Sobrien  return FAIL;
173260484Sobrien}
173360484Sobrien
173460484Sobrienstatic int
173560484Sobrienfp_reg_required_here (str, where)
173660484Sobrien     char ** str;
173760484Sobrien     int     where;
173860484Sobrien{
173960484Sobrien  int reg;
174060484Sobrien  char * start = *str;
174160484Sobrien
174260484Sobrien  if ((reg = arm_reg_parse (str)) != FAIL && fp_register (reg))
174360484Sobrien    {
174460484Sobrien      reg &= 7;
174560484Sobrien      inst.instruction |= reg << where;
174660484Sobrien      return reg;
174760484Sobrien    }
174860484Sobrien
174960484Sobrien  /* In the few cases where we might be able to accept something else
175060484Sobrien     this error can be overridden.  */
175160484Sobrien  inst.error = _("Floating point register expected");
175260484Sobrien
175360484Sobrien  /* Restore the start point.  */
175460484Sobrien  *str = start;
175560484Sobrien  return FAIL;
175660484Sobrien}
175760484Sobrien
175860484Sobrienstatic int
175960484Sobriencp_address_offset (str)
176060484Sobrien     char ** str;
176160484Sobrien{
176260484Sobrien  int offset;
176360484Sobrien
176460484Sobrien  skip_whitespace (* str);
176560484Sobrien
176660484Sobrien  if (! is_immediate_prefix (**str))
176760484Sobrien    {
176860484Sobrien      inst.error = _("immediate expression expected");
176960484Sobrien      return FAIL;
177060484Sobrien    }
177160484Sobrien
177260484Sobrien  (*str)++;
177360484Sobrien
177460484Sobrien  if (my_get_expression (& inst.reloc.exp, str))
177560484Sobrien    return FAIL;
177660484Sobrien
177760484Sobrien  if (inst.reloc.exp.X_op == O_constant)
177860484Sobrien    {
177960484Sobrien      offset = inst.reloc.exp.X_add_number;
178060484Sobrien
178160484Sobrien      if (offset & 3)
178260484Sobrien	{
178360484Sobrien	  inst.error = _("co-processor address must be word aligned");
178460484Sobrien	  return FAIL;
178560484Sobrien	}
178660484Sobrien
178760484Sobrien      if (offset > 1023 || offset < -1023)
178860484Sobrien	{
178960484Sobrien	  inst.error = _("offset too large");
179060484Sobrien	  return FAIL;
179160484Sobrien	}
179260484Sobrien
179360484Sobrien      if (offset >= 0)
179460484Sobrien	inst.instruction |= INDEX_UP;
179560484Sobrien      else
179660484Sobrien	offset = -offset;
179760484Sobrien
179860484Sobrien      inst.instruction |= offset >> 2;
179960484Sobrien    }
180060484Sobrien  else
180160484Sobrien    inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
180260484Sobrien
180360484Sobrien  return SUCCESS;
180460484Sobrien}
180560484Sobrien
180660484Sobrienstatic int
180760484Sobriencp_address_required_here (str)
180860484Sobrien     char ** str;
180960484Sobrien{
181060484Sobrien  char * p = * str;
181160484Sobrien  int    pre_inc = 0;
181260484Sobrien  int    write_back = 0;
181360484Sobrien
181460484Sobrien  if (*p == '[')
181560484Sobrien    {
181660484Sobrien      int reg;
181760484Sobrien
181860484Sobrien      p++;
181960484Sobrien      skip_whitespace (p);
182060484Sobrien
182160484Sobrien      if ((reg = reg_required_here (& p, 16)) == FAIL)
182260484Sobrien	return FAIL;
182360484Sobrien
182460484Sobrien      skip_whitespace (p);
182560484Sobrien
182660484Sobrien      if (*p == ']')
182760484Sobrien	{
182860484Sobrien	  p++;
182960484Sobrien
183060484Sobrien	  if (skip_past_comma (& p) == SUCCESS)
183160484Sobrien	    {
183260484Sobrien	      /* [Rn], #expr */
183360484Sobrien	      write_back = WRITE_BACK;
183460484Sobrien
183560484Sobrien	      if (reg == REG_PC)
183660484Sobrien		{
183760484Sobrien		  inst.error = _("pc may not be used in post-increment");
183860484Sobrien		  return FAIL;
183960484Sobrien		}
184060484Sobrien
184160484Sobrien	      if (cp_address_offset (& p) == FAIL)
184260484Sobrien		return FAIL;
184360484Sobrien	    }
184460484Sobrien	  else
184560484Sobrien	    pre_inc = PRE_INDEX | INDEX_UP;
184660484Sobrien	}
184760484Sobrien      else
184860484Sobrien	{
184960484Sobrien	  /* '['Rn, #expr']'[!] */
185060484Sobrien
185160484Sobrien	  if (skip_past_comma (& p) == FAIL)
185260484Sobrien	    {
185360484Sobrien	      inst.error = _("pre-indexed expression expected");
185460484Sobrien	      return FAIL;
185560484Sobrien	    }
185660484Sobrien
185760484Sobrien	  pre_inc = PRE_INDEX;
185860484Sobrien
185960484Sobrien	  if (cp_address_offset (& p) == FAIL)
186060484Sobrien	    return FAIL;
186160484Sobrien
186260484Sobrien	  skip_whitespace (p);
186360484Sobrien
186460484Sobrien	  if (*p++ != ']')
186560484Sobrien	    {
186660484Sobrien	      inst.error = _("missing ]");
186760484Sobrien	      return FAIL;
186860484Sobrien	    }
186960484Sobrien
187060484Sobrien	  skip_whitespace (p);
187160484Sobrien
187260484Sobrien	  if (*p == '!')
187360484Sobrien	    {
187460484Sobrien	      if (reg == REG_PC)
187560484Sobrien		{
187660484Sobrien		  inst.error = _("pc may not be used with write-back");
187760484Sobrien		  return FAIL;
187860484Sobrien		}
187960484Sobrien
188060484Sobrien	      p++;
188160484Sobrien	      write_back = WRITE_BACK;
188260484Sobrien	    }
188360484Sobrien	}
188460484Sobrien    }
188560484Sobrien  else
188660484Sobrien    {
188760484Sobrien      if (my_get_expression (&inst.reloc.exp, &p))
188860484Sobrien	return FAIL;
188960484Sobrien
189060484Sobrien      inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
189160484Sobrien      inst.reloc.exp.X_add_number -= 8;  /* PC rel adjust */
189260484Sobrien      inst.reloc.pc_rel = 1;
189360484Sobrien      inst.instruction |= (REG_PC << 16);
189460484Sobrien      pre_inc = PRE_INDEX;
189560484Sobrien    }
189660484Sobrien
189760484Sobrien  inst.instruction |= write_back | pre_inc;
189860484Sobrien  *str = p;
189960484Sobrien  return SUCCESS;
190060484Sobrien}
190160484Sobrien
190260484Sobrienstatic void
190360484Sobriendo_nop (str, flags)
190460484Sobrien     char * str;
190560484Sobrien     unsigned long flags;
190660484Sobrien{
190760484Sobrien  /* Do nothing really.  */
190860484Sobrien  inst.instruction |= flags; /* This is pointless.  */
190960484Sobrien  end_of_line (str);
191060484Sobrien  return;
191160484Sobrien}
191260484Sobrien
191360484Sobrienstatic void
191460484Sobriendo_mrs (str, flags)
191560484Sobrien     char *str;
191660484Sobrien     unsigned long flags;
191760484Sobrien{
191861843Sobrien  int skip = 0;
191961843Sobrien
192060484Sobrien  /* Only one syntax.  */
192160484Sobrien  skip_whitespace (str);
192260484Sobrien
192360484Sobrien  if (reg_required_here (&str, 12) == FAIL)
192460484Sobrien    {
192560484Sobrien      inst.error = BAD_ARGS;
192660484Sobrien      return;
192760484Sobrien    }
192860484Sobrien
192961843Sobrien  if (skip_past_comma (&str) == FAIL)
193060484Sobrien    {
193161843Sobrien      inst.error = _("comma expected after register name");
193260484Sobrien      return;
193360484Sobrien    }
193460484Sobrien
193561843Sobrien  skip_whitespace (str);
193661843Sobrien
193761843Sobrien  if (   strcmp (str, "CPSR") == 0
193861843Sobrien      || strcmp (str, "SPSR") == 0
193961843Sobrien	 /* Lower case versions for backwards compatability.  */
194061843Sobrien      || strcmp (str, "cpsr") == 0
194161843Sobrien      || strcmp (str, "spsr") == 0)
194261843Sobrien    skip = 4;
194361843Sobrien  /* This is for backwards compatability with older toolchains.  */
194461843Sobrien  else if (strcmp (str, "cpsr_all") == 0
194561843Sobrien	   || strcmp (str, "spsr_all") == 0)
194668765Sobrien    skip = 8;
194761843Sobrien  else
194861843Sobrien    {
194961843Sobrien      inst.error = _("{C|S}PSR expected");
195061843Sobrien      return;
195161843Sobrien    }
195261843Sobrien
195361843Sobrien  if (* str == 's' || * str == 'S')
195461843Sobrien    inst.instruction |= SPSR_BIT;
195561843Sobrien  str += skip;
195661843Sobrien
195760484Sobrien  inst.instruction |= flags;
195860484Sobrien  end_of_line (str);
195960484Sobrien}
196060484Sobrien
196161843Sobrien/* Two possible forms:
196261843Sobrien      "{C|S}PSR_<field>, Rm",
196361843Sobrien      "{C|S}PSR_f, #expression".  */
196460484Sobrienstatic void
196560484Sobriendo_msr (str, flags)
196660484Sobrien     char * str;
196760484Sobrien     unsigned long flags;
196860484Sobrien{
196961843Sobrien  skip_whitespace (str);
197060484Sobrien
197161843Sobrien  if (psr_required_here (& str) == FAIL)
197261843Sobrien    return;
197361843Sobrien
197461843Sobrien  if (skip_past_comma (& str) == FAIL)
197561843Sobrien    {
197661843Sobrien      inst.error = _("comma missing after psr flags");
197761843Sobrien      return;
197861843Sobrien    }
197961843Sobrien
198060484Sobrien  skip_whitespace (str);
198160484Sobrien
198261843Sobrien  if (reg_required_here (& str, 0) != FAIL)
198360484Sobrien    {
198461843Sobrien      inst.error = NULL;
198561843Sobrien      inst.instruction |= flags;
198661843Sobrien      end_of_line (str);
198761843Sobrien      return;
198861843Sobrien    }
198960484Sobrien
199061843Sobrien  if (! is_immediate_prefix (* str))
199161843Sobrien    {
199261843Sobrien      inst.error = _("only a register or immediate value can follow a psr flag");
199361843Sobrien      return;
199460484Sobrien    }
199561843Sobrien
199661843Sobrien  str ++;
199761843Sobrien  inst.error = NULL;
199861843Sobrien
199961843Sobrien  if (my_get_expression (& inst.reloc.exp, & str))
200061843Sobrien    {
200161843Sobrien      inst.error = _("only a register or immediate value can follow a psr flag");
200261843Sobrien      return;
200361843Sobrien    }
200461843Sobrien
200561843Sobrien  if (inst.instruction & ((PSR_c | PSR_x | PSR_s) << PSR_SHIFT))
200661843Sobrien    {
200768765Sobrien      inst.error = _("only flag field of psr can be set with immediate value");
200861843Sobrien      return;
200961843Sobrien    }
201061843Sobrien
201161843Sobrien  flags |= INST_IMMEDIATE;
201261843Sobrien
201361843Sobrien  if (inst.reloc.exp.X_add_symbol)
201461843Sobrien    {
201561843Sobrien      inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
201661843Sobrien      inst.reloc.pc_rel = 0;
201761843Sobrien    }
201860484Sobrien  else
201960484Sobrien    {
202061843Sobrien      unsigned value = validate_immediate (inst.reloc.exp.X_add_number);
202160484Sobrien
202261843Sobrien      if (value == (unsigned) FAIL)
202360484Sobrien	{
202461843Sobrien	  inst.error = _("Invalid constant");
202560484Sobrien	  return;
202660484Sobrien	}
202760484Sobrien
202861843Sobrien      inst.instruction |= value;
202960484Sobrien    }
203060484Sobrien
203160484Sobrien  inst.error = NULL;
203260484Sobrien  inst.instruction |= flags;
203360484Sobrien  end_of_line (str);
203460484Sobrien}
203560484Sobrien
203660484Sobrien/* Long Multiply Parser
203760484Sobrien   UMULL RdLo, RdHi, Rm, Rs
203860484Sobrien   SMULL RdLo, RdHi, Rm, Rs
203960484Sobrien   UMLAL RdLo, RdHi, Rm, Rs
204060484Sobrien   SMLAL RdLo, RdHi, Rm, Rs
204160484Sobrien*/
204260484Sobrienstatic void
204360484Sobriendo_mull (str, flags)
204460484Sobrien     char * str;
204560484Sobrien     unsigned long flags;
204660484Sobrien{
204760484Sobrien  int rdlo, rdhi, rm, rs;
204860484Sobrien
204960484Sobrien  /* Only one format "rdlo, rdhi, rm, rs" */
205060484Sobrien  skip_whitespace (str);
205160484Sobrien
205260484Sobrien  if ((rdlo = reg_required_here (&str, 12)) == FAIL)
205360484Sobrien    {
205460484Sobrien      inst.error = BAD_ARGS;
205560484Sobrien      return;
205660484Sobrien    }
205760484Sobrien
205860484Sobrien  if (skip_past_comma (&str) == FAIL
205960484Sobrien      || (rdhi = reg_required_here (&str, 16)) == FAIL)
206060484Sobrien    {
206160484Sobrien      inst.error = BAD_ARGS;
206260484Sobrien      return;
206360484Sobrien    }
206460484Sobrien
206560484Sobrien  if (skip_past_comma (&str) == FAIL
206660484Sobrien      || (rm = reg_required_here (&str, 0)) == FAIL)
206760484Sobrien    {
206860484Sobrien      inst.error = BAD_ARGS;
206960484Sobrien      return;
207060484Sobrien    }
207160484Sobrien
207260484Sobrien  /* rdhi, rdlo and rm must all be different */
207360484Sobrien  if (rdlo == rdhi || rdlo == rm || rdhi == rm)
207460484Sobrien    as_tsktsk (_("rdhi, rdlo and rm must all be different"));
207560484Sobrien
207660484Sobrien  if (skip_past_comma (&str) == FAIL
207760484Sobrien      || (rs = reg_required_here (&str, 8)) == FAIL)
207860484Sobrien    {
207960484Sobrien      inst.error = BAD_ARGS;
208060484Sobrien      return;
208160484Sobrien    }
208260484Sobrien
208360484Sobrien  if (rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC)
208460484Sobrien    {
208560484Sobrien      inst.error = BAD_PC;
208660484Sobrien      return;
208760484Sobrien    }
208860484Sobrien
208960484Sobrien  inst.instruction |= flags;
209060484Sobrien  end_of_line (str);
209160484Sobrien  return;
209260484Sobrien}
209360484Sobrien
209460484Sobrienstatic void
209560484Sobriendo_mul (str, flags)
209660484Sobrien     char *        str;
209760484Sobrien     unsigned long flags;
209860484Sobrien{
209960484Sobrien  int rd, rm;
210060484Sobrien
210160484Sobrien  /* Only one format "rd, rm, rs" */
210260484Sobrien  skip_whitespace (str);
210360484Sobrien
210460484Sobrien  if ((rd = reg_required_here (&str, 16)) == FAIL)
210560484Sobrien    {
210660484Sobrien      inst.error = BAD_ARGS;
210760484Sobrien      return;
210860484Sobrien    }
210960484Sobrien
211060484Sobrien  if (rd == REG_PC)
211160484Sobrien    {
211260484Sobrien      inst.error = BAD_PC;
211360484Sobrien      return;
211460484Sobrien    }
211560484Sobrien
211660484Sobrien  if (skip_past_comma (&str) == FAIL
211760484Sobrien      || (rm = reg_required_here (&str, 0)) == FAIL)
211860484Sobrien    {
211960484Sobrien      inst.error = BAD_ARGS;
212060484Sobrien      return;
212160484Sobrien    }
212260484Sobrien
212360484Sobrien  if (rm == REG_PC)
212460484Sobrien    {
212560484Sobrien      inst.error = BAD_PC;
212660484Sobrien      return;
212760484Sobrien    }
212860484Sobrien
212960484Sobrien  if (rm == rd)
213060484Sobrien    as_tsktsk (_("rd and rm should be different in mul"));
213160484Sobrien
213260484Sobrien  if (skip_past_comma (&str) == FAIL
213360484Sobrien      || (rm = reg_required_here (&str, 8)) == FAIL)
213460484Sobrien    {
213560484Sobrien      inst.error = BAD_ARGS;
213660484Sobrien      return;
213760484Sobrien    }
213860484Sobrien
213960484Sobrien  if (rm == REG_PC)
214060484Sobrien    {
214160484Sobrien      inst.error = BAD_PC;
214260484Sobrien      return;
214360484Sobrien    }
214460484Sobrien
214560484Sobrien  inst.instruction |= flags;
214660484Sobrien  end_of_line (str);
214760484Sobrien  return;
214860484Sobrien}
214960484Sobrien
215060484Sobrienstatic void
215160484Sobriendo_mla (str, flags)
215260484Sobrien     char *        str;
215360484Sobrien     unsigned long flags;
215460484Sobrien{
215560484Sobrien  int rd, rm;
215660484Sobrien
215760484Sobrien  /* Only one format "rd, rm, rs, rn" */
215860484Sobrien  skip_whitespace (str);
215960484Sobrien
216060484Sobrien  if ((rd = reg_required_here (&str, 16)) == FAIL)
216160484Sobrien    {
216260484Sobrien      inst.error = BAD_ARGS;
216360484Sobrien      return;
216460484Sobrien    }
216560484Sobrien
216660484Sobrien  if (rd == REG_PC)
216760484Sobrien    {
216860484Sobrien      inst.error = BAD_PC;
216960484Sobrien      return;
217060484Sobrien    }
217160484Sobrien
217260484Sobrien  if (skip_past_comma (&str) == FAIL
217360484Sobrien      || (rm = reg_required_here (&str, 0)) == FAIL)
217460484Sobrien    {
217560484Sobrien      inst.error = BAD_ARGS;
217660484Sobrien      return;
217760484Sobrien    }
217860484Sobrien
217960484Sobrien  if (rm == REG_PC)
218060484Sobrien    {
218160484Sobrien      inst.error = BAD_PC;
218260484Sobrien      return;
218360484Sobrien    }
218460484Sobrien
218560484Sobrien  if (rm == rd)
218660484Sobrien    as_tsktsk (_("rd and rm should be different in mla"));
218760484Sobrien
218860484Sobrien  if (skip_past_comma (&str) == FAIL
218960484Sobrien      || (rd = reg_required_here (&str, 8)) == FAIL
219060484Sobrien      || skip_past_comma (&str) == FAIL
219160484Sobrien      || (rm = reg_required_here (&str, 12)) == FAIL)
219260484Sobrien    {
219360484Sobrien      inst.error = BAD_ARGS;
219460484Sobrien      return;
219560484Sobrien    }
219660484Sobrien
219760484Sobrien  if (rd == REG_PC || rm == REG_PC)
219860484Sobrien    {
219960484Sobrien      inst.error = BAD_PC;
220060484Sobrien      return;
220160484Sobrien    }
220260484Sobrien
220360484Sobrien  inst.instruction |= flags;
220460484Sobrien  end_of_line (str);
220560484Sobrien  return;
220660484Sobrien}
220760484Sobrien
220860484Sobrien/* Returns the index into fp_values of a floating point number, or -1 if
220960484Sobrien   not in the table.  */
221060484Sobrienstatic int
221160484Sobrienmy_get_float_expression (str)
221260484Sobrien     char ** str;
221360484Sobrien{
221460484Sobrien  LITTLENUM_TYPE words[MAX_LITTLENUMS];
221560484Sobrien  char *         save_in;
221660484Sobrien  expressionS    exp;
221760484Sobrien  int            i;
221860484Sobrien  int            j;
221960484Sobrien
222060484Sobrien  memset (words, 0, MAX_LITTLENUMS * sizeof (LITTLENUM_TYPE));
222160484Sobrien  /* Look for a raw floating point number */
222260484Sobrien  if ((save_in = atof_ieee (*str, 'x', words)) != NULL
222360484Sobrien      && (is_end_of_line [(int)(*save_in)] || *save_in == '\0'))
222460484Sobrien    {
222560484Sobrien      for (i = 0; i < NUM_FLOAT_VALS; i++)
222660484Sobrien	{
222760484Sobrien	  for (j = 0; j < MAX_LITTLENUMS; j++)
222860484Sobrien	    {
222960484Sobrien	      if (words[j] != fp_values[i][j])
223060484Sobrien		break;
223160484Sobrien	    }
223260484Sobrien
223360484Sobrien	  if (j == MAX_LITTLENUMS)
223460484Sobrien	    {
223560484Sobrien	      *str = save_in;
223660484Sobrien	      return i;
223760484Sobrien	    }
223860484Sobrien	}
223960484Sobrien    }
224060484Sobrien
224160484Sobrien  /* Try and parse a more complex expression, this will probably fail
224260484Sobrien     unless the code uses a floating point prefix (eg "0f") */
224360484Sobrien  save_in = input_line_pointer;
224460484Sobrien  input_line_pointer = *str;
224560484Sobrien  if (expression (&exp) == absolute_section
224660484Sobrien      && exp.X_op == O_big
224760484Sobrien      && exp.X_add_number < 0)
224860484Sobrien    {
224960484Sobrien      /* FIXME: 5 = X_PRECISION, should be #define'd where we can use it.
225060484Sobrien	 Ditto for 15.  */
225160484Sobrien      if (gen_to_words (words, 5, (long)15) == 0)
225260484Sobrien	{
225360484Sobrien	  for (i = 0; i < NUM_FLOAT_VALS; i++)
225460484Sobrien	    {
225560484Sobrien	      for (j = 0; j < MAX_LITTLENUMS; j++)
225660484Sobrien		{
225760484Sobrien		  if (words[j] != fp_values[i][j])
225860484Sobrien		    break;
225960484Sobrien		}
226060484Sobrien
226160484Sobrien	      if (j == MAX_LITTLENUMS)
226260484Sobrien		{
226360484Sobrien		  *str = input_line_pointer;
226460484Sobrien		  input_line_pointer = save_in;
226560484Sobrien		  return i;
226660484Sobrien		}
226760484Sobrien	    }
226860484Sobrien	}
226960484Sobrien    }
227060484Sobrien
227160484Sobrien  *str = input_line_pointer;
227260484Sobrien  input_line_pointer = save_in;
227360484Sobrien  return -1;
227460484Sobrien}
227560484Sobrien
227660484Sobrien/* Return true if anything in the expression is a bignum */
227760484Sobrienstatic int
227860484Sobrienwalk_no_bignums (sp)
227960484Sobrien     symbolS * sp;
228060484Sobrien{
228160484Sobrien  if (symbol_get_value_expression (sp)->X_op == O_big)
228260484Sobrien    return 1;
228360484Sobrien
228460484Sobrien  if (symbol_get_value_expression (sp)->X_add_symbol)
228560484Sobrien    {
228660484Sobrien      return (walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol)
228760484Sobrien	      || (symbol_get_value_expression (sp)->X_op_symbol
228860484Sobrien		  && walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol)));
228960484Sobrien    }
229060484Sobrien
229160484Sobrien  return 0;
229260484Sobrien}
229360484Sobrien
229460484Sobrienstatic int
229560484Sobrienmy_get_expression (ep, str)
229660484Sobrien     expressionS * ep;
229760484Sobrien     char ** str;
229860484Sobrien{
229960484Sobrien  char * save_in;
230060484Sobrien  segT   seg;
230160484Sobrien
230260484Sobrien  save_in = input_line_pointer;
230360484Sobrien  input_line_pointer = *str;
230460484Sobrien  seg = expression (ep);
230560484Sobrien
230660484Sobrien#ifdef OBJ_AOUT
230760484Sobrien  if (seg != absolute_section
230860484Sobrien      && seg != text_section
230960484Sobrien      && seg != data_section
231060484Sobrien      && seg != bss_section
231160484Sobrien      && seg != undefined_section)
231260484Sobrien    {
231360484Sobrien      inst.error = _("bad_segment");
231460484Sobrien      *str = input_line_pointer;
231560484Sobrien      input_line_pointer = save_in;
231660484Sobrien      return 1;
231760484Sobrien    }
231860484Sobrien#endif
231960484Sobrien
232060484Sobrien  /* Get rid of any bignums now, so that we don't generate an error for which
232160484Sobrien     we can't establish a line number later on.  Big numbers are never valid
232260484Sobrien     in instructions, which is where this routine is always called.  */
232360484Sobrien  if (ep->X_op == O_big
232460484Sobrien      || (ep->X_add_symbol
232560484Sobrien	  && (walk_no_bignums (ep->X_add_symbol)
232660484Sobrien	      || (ep->X_op_symbol
232760484Sobrien		  && walk_no_bignums (ep->X_op_symbol)))))
232860484Sobrien    {
232960484Sobrien      inst.error = _("Invalid constant");
233060484Sobrien      *str = input_line_pointer;
233160484Sobrien      input_line_pointer = save_in;
233260484Sobrien      return 1;
233360484Sobrien    }
233460484Sobrien
233560484Sobrien  *str = input_line_pointer;
233660484Sobrien  input_line_pointer = save_in;
233760484Sobrien  return 0;
233860484Sobrien}
233960484Sobrien
234060484Sobrien/* unrestrict should be one if <shift> <register> is permitted for this
234160484Sobrien   instruction */
234260484Sobrien
234360484Sobrienstatic int
234460484Sobriendecode_shift (str, unrestrict)
234560484Sobrien     char ** str;
234660484Sobrien     int     unrestrict;
234760484Sobrien{
234860484Sobrien  struct asm_shift * shft;
234960484Sobrien  char * p;
235060484Sobrien  char   c;
235160484Sobrien
235260484Sobrien  skip_whitespace (* str);
235360484Sobrien
235460484Sobrien  for (p = *str; isalpha (*p); p++)
235560484Sobrien    ;
235660484Sobrien
235760484Sobrien  if (p == *str)
235860484Sobrien    {
235960484Sobrien      inst.error = _("Shift expression expected");
236060484Sobrien      return FAIL;
236160484Sobrien    }
236260484Sobrien
236360484Sobrien  c = *p;
236460484Sobrien  *p = '\0';
236560484Sobrien  shft = (struct asm_shift *) hash_find (arm_shift_hsh, *str);
236660484Sobrien  *p = c;
236760484Sobrien  if (shft)
236860484Sobrien    {
236960484Sobrien      if (!strncmp (*str, "rrx", 3)
237060484Sobrien          || !strncmp (*str, "RRX", 3))
237160484Sobrien	{
237260484Sobrien	  *str = p;
237360484Sobrien	  inst.instruction |= shft->value;
237460484Sobrien	  return SUCCESS;
237560484Sobrien	}
237660484Sobrien
237760484Sobrien      skip_whitespace (p);
237860484Sobrien
237960484Sobrien      if (unrestrict && reg_required_here (&p, 8) != FAIL)
238060484Sobrien	{
238160484Sobrien	  inst.instruction |= shft->value | SHIFT_BY_REG;
238260484Sobrien	  *str = p;
238360484Sobrien	  return SUCCESS;
238460484Sobrien	}
238560484Sobrien      else if (is_immediate_prefix (* p))
238660484Sobrien	{
238760484Sobrien	  inst.error = NULL;
238860484Sobrien	  p++;
238960484Sobrien	  if (my_get_expression (&inst.reloc.exp, &p))
239060484Sobrien	    return FAIL;
239160484Sobrien
239260484Sobrien	  /* Validate some simple #expressions */
239360484Sobrien	  if (inst.reloc.exp.X_op == O_constant)
239460484Sobrien	    {
239560484Sobrien	      unsigned num = inst.reloc.exp.X_add_number;
239660484Sobrien
239760484Sobrien	      /* Reject operations greater than 32, or lsl #32 */
239860484Sobrien	      if (num > 32 || (num == 32 && shft->value == 0))
239960484Sobrien		{
240060484Sobrien		  inst.error = _("Invalid immediate shift");
240160484Sobrien		  return FAIL;
240260484Sobrien		}
240360484Sobrien
240460484Sobrien	      /* Shifts of zero should be converted to lsl (which is zero)*/
240560484Sobrien	      if (num == 0)
240660484Sobrien		{
240760484Sobrien		  *str = p;
240860484Sobrien		  return SUCCESS;
240960484Sobrien		}
241060484Sobrien
241160484Sobrien	      /* Shifts of 32 are encoded as 0, for those shifts that
241260484Sobrien		 support it.  */
241360484Sobrien	      if (num == 32)
241460484Sobrien		num = 0;
241560484Sobrien
241660484Sobrien	      inst.instruction |= (num << 7) | shft->value;
241760484Sobrien	      *str = p;
241860484Sobrien	      return SUCCESS;
241960484Sobrien	    }
242060484Sobrien
242160484Sobrien	  inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
242260484Sobrien	  inst.reloc.pc_rel = 0;
242360484Sobrien	  inst.instruction |= shft->value;
242460484Sobrien	  *str = p;
242560484Sobrien	  return SUCCESS;
242660484Sobrien	}
242760484Sobrien      else
242860484Sobrien	{
242960484Sobrien	  inst.error = unrestrict ? _("shift requires register or #expression")
243060484Sobrien	    : _("shift requires #expression");
243160484Sobrien	  *str = p;
243260484Sobrien	  return FAIL;
243360484Sobrien	}
243460484Sobrien    }
243560484Sobrien
243660484Sobrien  inst.error = _("Shift expression expected");
243760484Sobrien  return FAIL;
243860484Sobrien}
243960484Sobrien
244060484Sobrien/* Do those data_ops which can take a negative immediate constant */
244160484Sobrien/* by altering the instuction. A bit of a hack really */
244260484Sobrien/*      MOV <-> MVN
244360484Sobrien        AND <-> BIC
244460484Sobrien        ADC <-> SBC
244560484Sobrien        by inverting the second operand, and
244660484Sobrien        ADD <-> SUB
244760484Sobrien        CMP <-> CMN
244860484Sobrien        by negating the second operand.
244960484Sobrien*/
245060484Sobrienstatic int
245160484Sobriennegate_data_op (instruction, value)
245260484Sobrien     unsigned long * instruction;
245360484Sobrien     unsigned long   value;
245460484Sobrien{
245560484Sobrien  int op, new_inst;
245660484Sobrien  unsigned long negated, inverted;
245760484Sobrien
245860484Sobrien  negated = validate_immediate (-value);
245960484Sobrien  inverted = validate_immediate (~value);
246060484Sobrien
246160484Sobrien  op = (*instruction >> DATA_OP_SHIFT) & 0xf;
246260484Sobrien  switch (op)
246360484Sobrien    {
246460484Sobrien      /* First negates */
246560484Sobrien    case OPCODE_SUB:             /* ADD <-> SUB */
246660484Sobrien      new_inst = OPCODE_ADD;
246760484Sobrien      value = negated;
246860484Sobrien      break;
246960484Sobrien
247060484Sobrien    case OPCODE_ADD:
247160484Sobrien      new_inst = OPCODE_SUB;
247260484Sobrien      value = negated;
247360484Sobrien      break;
247460484Sobrien
247560484Sobrien    case OPCODE_CMP:             /* CMP <-> CMN */
247660484Sobrien      new_inst = OPCODE_CMN;
247760484Sobrien      value = negated;
247860484Sobrien      break;
247960484Sobrien
248060484Sobrien    case OPCODE_CMN:
248160484Sobrien      new_inst = OPCODE_CMP;
248260484Sobrien      value = negated;
248360484Sobrien      break;
248460484Sobrien
248560484Sobrien      /* Now Inverted ops */
248660484Sobrien    case OPCODE_MOV:             /* MOV <-> MVN */
248760484Sobrien      new_inst = OPCODE_MVN;
248860484Sobrien      value = inverted;
248960484Sobrien      break;
249060484Sobrien
249160484Sobrien    case OPCODE_MVN:
249260484Sobrien      new_inst = OPCODE_MOV;
249360484Sobrien      value = inverted;
249460484Sobrien      break;
249560484Sobrien
249660484Sobrien    case OPCODE_AND:             /* AND <-> BIC */
249760484Sobrien      new_inst = OPCODE_BIC;
249860484Sobrien      value = inverted;
249960484Sobrien      break;
250060484Sobrien
250160484Sobrien    case OPCODE_BIC:
250260484Sobrien      new_inst = OPCODE_AND;
250360484Sobrien      value = inverted;
250460484Sobrien      break;
250560484Sobrien
250660484Sobrien    case OPCODE_ADC:              /* ADC <-> SBC */
250760484Sobrien      new_inst = OPCODE_SBC;
250860484Sobrien      value = inverted;
250960484Sobrien      break;
251060484Sobrien
251160484Sobrien    case OPCODE_SBC:
251260484Sobrien      new_inst = OPCODE_ADC;
251360484Sobrien      value = inverted;
251460484Sobrien      break;
251560484Sobrien
251660484Sobrien      /* We cannot do anything */
251760484Sobrien    default:
251860484Sobrien      return FAIL;
251960484Sobrien    }
252060484Sobrien
252160484Sobrien  if (value == (unsigned) FAIL)
252260484Sobrien    return FAIL;
252360484Sobrien
252460484Sobrien  *instruction &= OPCODE_MASK;
252560484Sobrien  *instruction |= new_inst << DATA_OP_SHIFT;
252660484Sobrien  return value;
252760484Sobrien}
252860484Sobrien
252960484Sobrienstatic int
253060484Sobriendata_op2 (str)
253160484Sobrien     char ** str;
253260484Sobrien{
253360484Sobrien  int value;
253460484Sobrien  expressionS expr;
253560484Sobrien
253660484Sobrien  skip_whitespace (* str);
253760484Sobrien
253860484Sobrien  if (reg_required_here (str, 0) != FAIL)
253960484Sobrien    {
254060484Sobrien      if (skip_past_comma (str) == SUCCESS)
254160484Sobrien	/* Shift operation on register.  */
254260484Sobrien	return decode_shift (str, NO_SHIFT_RESTRICT);
254360484Sobrien
254460484Sobrien      return SUCCESS;
254560484Sobrien    }
254660484Sobrien  else
254760484Sobrien    {
254860484Sobrien      /* Immediate expression */
254960484Sobrien      if (is_immediate_prefix (**str))
255060484Sobrien	{
255160484Sobrien	  (*str)++;
255260484Sobrien	  inst.error = NULL;
255360484Sobrien
255460484Sobrien	  if (my_get_expression (&inst.reloc.exp, str))
255560484Sobrien	    return FAIL;
255660484Sobrien
255760484Sobrien	  if (inst.reloc.exp.X_add_symbol)
255860484Sobrien	    {
255960484Sobrien	      inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
256060484Sobrien	      inst.reloc.pc_rel = 0;
256160484Sobrien	    }
256260484Sobrien	  else
256360484Sobrien	    {
256460484Sobrien	      if (skip_past_comma (str) == SUCCESS)
256560484Sobrien		{
256660484Sobrien		  /* #x, y -- ie explicit rotation by Y  */
256760484Sobrien		  if (my_get_expression (&expr, str))
256860484Sobrien		    return FAIL;
256960484Sobrien
257060484Sobrien		  if (expr.X_op != O_constant)
257160484Sobrien		    {
257260484Sobrien		      inst.error = _("Constant expression expected");
257360484Sobrien		      return FAIL;
257460484Sobrien		    }
257560484Sobrien
257660484Sobrien		  /* Rotate must be a multiple of 2 */
257760484Sobrien		  if (((unsigned) expr.X_add_number) > 30
257860484Sobrien		      || (expr.X_add_number & 1) != 0
257960484Sobrien		      || ((unsigned) inst.reloc.exp.X_add_number) > 255)
258060484Sobrien		    {
258160484Sobrien		      inst.error = _("Invalid constant");
258260484Sobrien		      return FAIL;
258360484Sobrien		    }
258460484Sobrien		  inst.instruction |= INST_IMMEDIATE;
258560484Sobrien		  inst.instruction |= inst.reloc.exp.X_add_number;
258660484Sobrien		  inst.instruction |= expr.X_add_number << 7;
258760484Sobrien		  return SUCCESS;
258860484Sobrien		}
258960484Sobrien
259060484Sobrien	      /* Implicit rotation, select a suitable one  */
259160484Sobrien	      value = validate_immediate (inst.reloc.exp.X_add_number);
259260484Sobrien
259360484Sobrien	      if (value == FAIL)
259460484Sobrien		{
259560484Sobrien		  /* Can't be done, perhaps the code reads something like
259660484Sobrien		     "add Rd, Rn, #-n", where "sub Rd, Rn, #n" would be ok */
259760484Sobrien		  if ((value = negate_data_op (&inst.instruction,
259860484Sobrien					       inst.reloc.exp.X_add_number))
259960484Sobrien		      == FAIL)
260060484Sobrien		    {
260160484Sobrien		      inst.error = _("Invalid constant");
260260484Sobrien		      return FAIL;
260360484Sobrien		    }
260460484Sobrien		}
260560484Sobrien
260660484Sobrien	      inst.instruction |= value;
260760484Sobrien	    }
260860484Sobrien
260960484Sobrien	  inst.instruction |= INST_IMMEDIATE;
261060484Sobrien	  return SUCCESS;
261160484Sobrien	}
261260484Sobrien
261360484Sobrien      (*str)++;
261460484Sobrien      inst.error = _("Register or shift expression expected");
261560484Sobrien      return FAIL;
261660484Sobrien    }
261760484Sobrien}
261860484Sobrien
261960484Sobrienstatic int
262060484Sobrienfp_op2 (str)
262160484Sobrien     char ** str;
262260484Sobrien{
262360484Sobrien  skip_whitespace (* str);
262460484Sobrien
262560484Sobrien  if (fp_reg_required_here (str, 0) != FAIL)
262660484Sobrien    return SUCCESS;
262760484Sobrien  else
262860484Sobrien    {
262960484Sobrien      /* Immediate expression */
263060484Sobrien      if (*((*str)++) == '#')
263160484Sobrien	{
263260484Sobrien	  int i;
263360484Sobrien
263460484Sobrien	  inst.error = NULL;
263560484Sobrien
263660484Sobrien	  skip_whitespace (* str);
263760484Sobrien
263860484Sobrien	  /* First try and match exact strings, this is to guarantee that
263960484Sobrien	     some formats will work even for cross assembly */
264060484Sobrien
264160484Sobrien	  for (i = 0; fp_const[i]; i++)
264260484Sobrien	    {
264360484Sobrien	      if (strncmp (*str, fp_const[i], strlen (fp_const[i])) == 0)
264460484Sobrien		{
264560484Sobrien		  char *start = *str;
264660484Sobrien
264760484Sobrien		  *str += strlen (fp_const[i]);
264860484Sobrien		  if (is_end_of_line[(int)**str] || **str == '\0')
264960484Sobrien		    {
265060484Sobrien		      inst.instruction |= i + 8;
265160484Sobrien		      return SUCCESS;
265260484Sobrien		    }
265360484Sobrien		  *str = start;
265460484Sobrien		}
265560484Sobrien	    }
265660484Sobrien
265760484Sobrien	  /* Just because we didn't get a match doesn't mean that the
265860484Sobrien	     constant isn't valid, just that it is in a format that we
265960484Sobrien	     don't automatically recognize.  Try parsing it with
266060484Sobrien	     the standard expression routines.  */
266160484Sobrien	  if ((i = my_get_float_expression (str)) >= 0)
266260484Sobrien	    {
266360484Sobrien	      inst.instruction |= i + 8;
266460484Sobrien	      return SUCCESS;
266560484Sobrien	    }
266660484Sobrien
266760484Sobrien	  inst.error = _("Invalid floating point immediate expression");
266860484Sobrien	  return FAIL;
266960484Sobrien	}
267060484Sobrien      inst.error = _("Floating point register or immediate expression expected");
267160484Sobrien      return FAIL;
267260484Sobrien    }
267360484Sobrien}
267460484Sobrien
267560484Sobrienstatic void
267660484Sobriendo_arit (str, flags)
267760484Sobrien     char *        str;
267860484Sobrien     unsigned long flags;
267960484Sobrien{
268060484Sobrien  skip_whitespace (str);
268160484Sobrien
268260484Sobrien  if (reg_required_here (&str, 12) == FAIL
268360484Sobrien      || skip_past_comma (&str) == FAIL
268460484Sobrien      || reg_required_here (&str, 16) == FAIL
268560484Sobrien      || skip_past_comma (&str) == FAIL
268660484Sobrien      || data_op2 (&str) == FAIL)
268760484Sobrien    {
268860484Sobrien      if (!inst.error)
268960484Sobrien	inst.error = BAD_ARGS;
269060484Sobrien      return;
269160484Sobrien    }
269260484Sobrien
269360484Sobrien  inst.instruction |= flags;
269460484Sobrien  end_of_line (str);
269560484Sobrien  return;
269660484Sobrien}
269760484Sobrien
269860484Sobrienstatic void
269960484Sobriendo_adr (str, flags)
270060484Sobrien     char *        str;
270160484Sobrien     unsigned long flags;
270260484Sobrien{
270360484Sobrien  /* This is a pseudo-op of the form "adr rd, label" to be converted
270460484Sobrien     into a relative address of the form "add rd, pc, #label-.-8".  */
270560484Sobrien  skip_whitespace (str);
270660484Sobrien
270760484Sobrien  if (reg_required_here (&str, 12) == FAIL
270860484Sobrien      || skip_past_comma (&str) == FAIL
270960484Sobrien      || my_get_expression (&inst.reloc.exp, &str))
271060484Sobrien    {
271160484Sobrien      if (!inst.error)
271260484Sobrien	inst.error = BAD_ARGS;
271360484Sobrien      return;
271460484Sobrien    }
271560484Sobrien
271660484Sobrien  /* Frag hacking will turn this into a sub instruction if the offset turns
271760484Sobrien     out to be negative.  */
271860484Sobrien  inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
271960484Sobrien  inst.reloc.exp.X_add_number -= 8; /* PC relative adjust.  */
272060484Sobrien  inst.reloc.pc_rel = 1;
272160484Sobrien  inst.instruction |= flags;
272260484Sobrien  end_of_line (str);
272360484Sobrien  return;
272460484Sobrien}
272560484Sobrien
272660484Sobrienstatic void
272760484Sobriendo_adrl (str, flags)
272860484Sobrien     char *        str;
272960484Sobrien     unsigned long flags;
273060484Sobrien{
273160484Sobrien  /* This is a pseudo-op of the form "adrl rd, label" to be converted
273260484Sobrien     into a relative address of the form:
273360484Sobrien     	add rd, pc, #low(label-.-8)"
273460484Sobrien     	add rd, rd, #high(label-.-8)"   */
273560484Sobrien
273660484Sobrien  skip_whitespace (str);
273760484Sobrien
273860484Sobrien  if (reg_required_here (& str, 12) == FAIL
273960484Sobrien      || skip_past_comma (& str) == FAIL
274060484Sobrien      || my_get_expression (& inst.reloc.exp, & str))
274160484Sobrien    {
274260484Sobrien      if (!inst.error)
274360484Sobrien	inst.error = BAD_ARGS;
274460484Sobrien      return;
274560484Sobrien    }
274660484Sobrien
274760484Sobrien  end_of_line (str);
274860484Sobrien
274960484Sobrien  /* Frag hacking will turn this into a sub instruction if the offset turns
275060484Sobrien     out to be negative.  */
275160484Sobrien  inst.reloc.type              = BFD_RELOC_ARM_ADRL_IMMEDIATE;
275260484Sobrien  inst.reloc.exp.X_add_number -= 8; /* PC relative adjust */
275360484Sobrien  inst.reloc.pc_rel            = 1;
275460484Sobrien  inst.instruction            |= flags;
275560484Sobrien  inst.size                    = INSN_SIZE * 2;
275660484Sobrien
275760484Sobrien  return;
275860484Sobrien}
275960484Sobrien
276060484Sobrienstatic void
276160484Sobriendo_cmp (str, flags)
276260484Sobrien     char *        str;
276360484Sobrien     unsigned long flags;
276460484Sobrien{
276560484Sobrien  skip_whitespace (str);
276660484Sobrien
276760484Sobrien  if (reg_required_here (&str, 16) == FAIL)
276860484Sobrien    {
276960484Sobrien      if (!inst.error)
277060484Sobrien	inst.error = BAD_ARGS;
277160484Sobrien      return;
277260484Sobrien    }
277360484Sobrien
277460484Sobrien  if (skip_past_comma (&str) == FAIL
277560484Sobrien      || data_op2 (&str) == FAIL)
277660484Sobrien    {
277760484Sobrien      if (!inst.error)
277860484Sobrien	inst.error = BAD_ARGS;
277960484Sobrien      return;
278060484Sobrien    }
278160484Sobrien
278260484Sobrien  inst.instruction |= flags;
278360484Sobrien  if ((flags & 0x0000f000) == 0)
278460484Sobrien    inst.instruction |= CONDS_BIT;
278560484Sobrien
278660484Sobrien  end_of_line (str);
278760484Sobrien  return;
278860484Sobrien}
278960484Sobrien
279060484Sobrienstatic void
279160484Sobriendo_mov (str, flags)
279260484Sobrien     char *        str;
279360484Sobrien     unsigned long flags;
279460484Sobrien{
279560484Sobrien  skip_whitespace (str);
279660484Sobrien
279760484Sobrien  if (reg_required_here (&str, 12) == FAIL)
279860484Sobrien    {
279960484Sobrien      if (!inst.error)
280060484Sobrien	inst.error = BAD_ARGS;
280160484Sobrien      return;
280260484Sobrien    }
280360484Sobrien
280460484Sobrien  if (skip_past_comma (&str) == FAIL
280560484Sobrien      || data_op2 (&str) == FAIL)
280660484Sobrien    {
280760484Sobrien      if (!inst.error)
280860484Sobrien	inst.error = BAD_ARGS;
280960484Sobrien      return;
281060484Sobrien    }
281160484Sobrien
281260484Sobrien  inst.instruction |= flags;
281360484Sobrien  end_of_line (str);
281460484Sobrien  return;
281560484Sobrien}
281660484Sobrien
281760484Sobrienstatic int
281860484Sobrienldst_extend (str, hwse)
281960484Sobrien     char ** str;
282060484Sobrien     int     hwse;
282160484Sobrien{
282260484Sobrien  int add = INDEX_UP;
282360484Sobrien
282460484Sobrien  switch (**str)
282560484Sobrien    {
282660484Sobrien    case '#':
282760484Sobrien    case '$':
282860484Sobrien      (*str)++;
282960484Sobrien      if (my_get_expression (& inst.reloc.exp, str))
283060484Sobrien	return FAIL;
283160484Sobrien
283260484Sobrien      if (inst.reloc.exp.X_op == O_constant)
283360484Sobrien	{
283460484Sobrien	  int value = inst.reloc.exp.X_add_number;
283560484Sobrien
283660484Sobrien          if ((hwse && (value < -255 || value > 255))
283760484Sobrien               || (value < -4095 || value > 4095))
283860484Sobrien	    {
283960484Sobrien	      inst.error = _("address offset too large");
284060484Sobrien	      return FAIL;
284160484Sobrien	    }
284260484Sobrien
284360484Sobrien	  if (value < 0)
284460484Sobrien	    {
284560484Sobrien	      value = -value;
284660484Sobrien	      add = 0;
284760484Sobrien	    }
284860484Sobrien
284960484Sobrien          /* Halfword and signextension instructions have the
285060484Sobrien             immediate value split across bits 11..8 and bits 3..0 */
285160484Sobrien          if (hwse)
285260484Sobrien            inst.instruction |= add | HWOFFSET_IMM | ((value >> 4) << 8) | (value & 0xF);
285360484Sobrien          else
285460484Sobrien            inst.instruction |= add | value;
285560484Sobrien	}
285660484Sobrien      else
285760484Sobrien	{
285860484Sobrien          if (hwse)
285960484Sobrien            {
286060484Sobrien              inst.instruction |= HWOFFSET_IMM;
286160484Sobrien              inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
286260484Sobrien            }
286360484Sobrien          else
286460484Sobrien            inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
286560484Sobrien	  inst.reloc.pc_rel = 0;
286660484Sobrien	}
286760484Sobrien      return SUCCESS;
286860484Sobrien
286960484Sobrien    case '-':
287060484Sobrien      add = 0;	/* and fall through */
287160484Sobrien    case '+':
287260484Sobrien      (*str)++;	/* and fall through */
287360484Sobrien    default:
287460484Sobrien      if (reg_required_here (str, 0) == FAIL)
287560484Sobrien	return FAIL;
287660484Sobrien
287760484Sobrien      if (hwse)
287860484Sobrien        inst.instruction |= add;
287960484Sobrien      else
288060484Sobrien        {
288160484Sobrien          inst.instruction |= add | OFFSET_REG;
288260484Sobrien          if (skip_past_comma (str) == SUCCESS)
288360484Sobrien            return decode_shift (str, SHIFT_RESTRICT);
288460484Sobrien        }
288560484Sobrien
288660484Sobrien      return SUCCESS;
288760484Sobrien    }
288860484Sobrien}
288960484Sobrien
289060484Sobrienstatic void
289160484Sobriendo_ldst (str, flags)
289260484Sobrien     char *        str;
289360484Sobrien     unsigned long flags;
289460484Sobrien{
289560484Sobrien  int halfword = 0;
289660484Sobrien  int pre_inc = 0;
289760484Sobrien  int conflict_reg;
289860484Sobrien  int value;
289960484Sobrien
290060484Sobrien  /* This is not ideal, but it is the simplest way of dealing with the
290160484Sobrien     ARM7T halfword instructions (since they use a different
290260484Sobrien     encoding, but the same mnemonic): */
290360484Sobrien  halfword = (flags & 0x80000000) != 0;
290460484Sobrien  if (halfword)
290560484Sobrien    {
290660484Sobrien      /* This is actually a load/store of a halfword, or a
290760484Sobrien         signed-extension load */
290860484Sobrien      if ((cpu_variant & ARM_HALFWORD) == 0)
290960484Sobrien        {
291060484Sobrien          inst.error
291160484Sobrien	    = _("Processor does not support halfwords or signed bytes");
291260484Sobrien          return;
291360484Sobrien        }
291460484Sobrien
291560484Sobrien      inst.instruction = (inst.instruction & COND_MASK)
291660484Sobrien                         | (flags & ~COND_MASK);
291760484Sobrien
291860484Sobrien      flags = 0;
291960484Sobrien    }
292060484Sobrien
292160484Sobrien  skip_whitespace (str);
292260484Sobrien
292360484Sobrien  if ((conflict_reg = reg_required_here (& str, 12)) == FAIL)
292460484Sobrien    {
292560484Sobrien      if (!inst.error)
292660484Sobrien	inst.error = BAD_ARGS;
292760484Sobrien      return;
292860484Sobrien    }
292960484Sobrien
293060484Sobrien  if (skip_past_comma (& str) == FAIL)
293160484Sobrien    {
293260484Sobrien      inst.error = _("Address expected");
293360484Sobrien      return;
293460484Sobrien    }
293560484Sobrien
293660484Sobrien  if (*str == '[')
293760484Sobrien    {
293860484Sobrien      int reg;
293960484Sobrien
294060484Sobrien      str++;
294160484Sobrien
294260484Sobrien      skip_whitespace (str);
294360484Sobrien
294460484Sobrien      if ((reg = reg_required_here (&str, 16)) == FAIL)
294560484Sobrien	return;
294660484Sobrien
294760484Sobrien      /* Conflicts can occur on stores as well as loads.  */
294860484Sobrien      conflict_reg = (conflict_reg == reg);
294960484Sobrien
295060484Sobrien      skip_whitespace (str);
295160484Sobrien
295260484Sobrien      if (*str == ']')
295360484Sobrien	{
295460484Sobrien	  str ++;
295560484Sobrien
295660484Sobrien	  if (skip_past_comma (&str) == SUCCESS)
295760484Sobrien	    {
295860484Sobrien	      /* [Rn],... (post inc) */
295960484Sobrien	      if (ldst_extend (&str, halfword) == FAIL)
296060484Sobrien		return;
296160484Sobrien	      if (conflict_reg)
296260484Sobrien		as_warn (_("%s register same as write-back base"),
296360484Sobrien			 (inst.instruction & LOAD_BIT) ? _("destination") : _("source") );
296460484Sobrien	    }
296560484Sobrien	  else
296660484Sobrien	    {
296760484Sobrien	      /* [Rn] */
296860484Sobrien              if (halfword)
296960484Sobrien                inst.instruction |= HWOFFSET_IMM;
297060484Sobrien
297160484Sobrien              skip_whitespace (str);
297260484Sobrien
297360484Sobrien              if (*str == '!')
297460484Sobrien               {
297560484Sobrien                 if (conflict_reg)
297660484Sobrien		   as_warn (_("%s register same as write-back base"),
297760484Sobrien			    (inst.instruction & LOAD_BIT) ? _("destination") : _("source") );
297860484Sobrien                 str++;
297960484Sobrien                 inst.instruction |= WRITE_BACK;
298060484Sobrien               }
298160484Sobrien
298260484Sobrien	      flags |= INDEX_UP;
298360484Sobrien	      if (! (flags & TRANS_BIT))
298460484Sobrien		pre_inc = 1;
298560484Sobrien	    }
298660484Sobrien	}
298760484Sobrien      else
298860484Sobrien	{
298960484Sobrien	  /* [Rn,...] */
299060484Sobrien	  if (skip_past_comma (&str) == FAIL)
299160484Sobrien	    {
299260484Sobrien	      inst.error = _("pre-indexed expression expected");
299360484Sobrien	      return;
299460484Sobrien	    }
299560484Sobrien
299660484Sobrien	  pre_inc = 1;
299760484Sobrien	  if (ldst_extend (&str, halfword) == FAIL)
299860484Sobrien	    return;
299960484Sobrien
300060484Sobrien	  skip_whitespace (str);
300160484Sobrien
300260484Sobrien	  if (*str++ != ']')
300360484Sobrien	    {
300460484Sobrien	      inst.error = _("missing ]");
300560484Sobrien	      return;
300660484Sobrien	    }
300760484Sobrien
300860484Sobrien	  skip_whitespace (str);
300960484Sobrien
301060484Sobrien	  if (*str == '!')
301160484Sobrien	    {
301260484Sobrien	      if (conflict_reg)
301360484Sobrien		as_warn (_("%s register same as write-back base"),
301460484Sobrien			 (inst.instruction & LOAD_BIT) ? _("destination") : _("source") );
301560484Sobrien	      str++;
301660484Sobrien	      inst.instruction |= WRITE_BACK;
301760484Sobrien	    }
301860484Sobrien	}
301960484Sobrien    }
302060484Sobrien  else if (*str == '=')
302160484Sobrien    {
302260484Sobrien      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */
302360484Sobrien      str++;
302460484Sobrien
302560484Sobrien      skip_whitespace (str);
302660484Sobrien
302760484Sobrien      if (my_get_expression (&inst.reloc.exp, &str))
302860484Sobrien	return;
302960484Sobrien
303060484Sobrien      if (inst.reloc.exp.X_op != O_constant
303160484Sobrien	  && inst.reloc.exp.X_op != O_symbol)
303260484Sobrien	{
303360484Sobrien	  inst.error = _("Constant expression expected");
303460484Sobrien	  return;
303560484Sobrien	}
303660484Sobrien
303760484Sobrien      if (inst.reloc.exp.X_op == O_constant
303860484Sobrien	  && (value = validate_immediate(inst.reloc.exp.X_add_number)) != FAIL)
303960484Sobrien	{
304060484Sobrien	  /* This can be done with a mov instruction */
304160484Sobrien	  inst.instruction &= LITERAL_MASK;
304260484Sobrien	  inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
304360484Sobrien	  inst.instruction |= (flags & COND_MASK) | (value & 0xfff);
304460484Sobrien	  end_of_line(str);
304560484Sobrien	  return;
304660484Sobrien	}
304760484Sobrien      else
304860484Sobrien	{
304960484Sobrien	  /* Insert into literal pool */
305060484Sobrien	  if (add_to_lit_pool () == FAIL)
305160484Sobrien	    {
305260484Sobrien	      if (!inst.error)
305360484Sobrien		inst.error = _("literal pool insertion failed");
305460484Sobrien	      return;
305560484Sobrien	    }
305660484Sobrien
305760484Sobrien	  /* Change the instruction exp to point to the pool */
305860484Sobrien          if (halfword)
305960484Sobrien            {
306060484Sobrien              inst.instruction |= HWOFFSET_IMM;
306160484Sobrien              inst.reloc.type = BFD_RELOC_ARM_HWLITERAL;
306260484Sobrien            }
306360484Sobrien          else
306460484Sobrien	    inst.reloc.type = BFD_RELOC_ARM_LITERAL;
306560484Sobrien	  inst.reloc.pc_rel = 1;
306660484Sobrien	  inst.instruction |= (REG_PC << 16);
306760484Sobrien	  pre_inc = 1;
306860484Sobrien	}
306960484Sobrien    }
307060484Sobrien  else
307160484Sobrien    {
307260484Sobrien      if (my_get_expression (&inst.reloc.exp, &str))
307360484Sobrien	return;
307460484Sobrien
307560484Sobrien      if (halfword)
307660484Sobrien        {
307760484Sobrien          inst.instruction |= HWOFFSET_IMM;
307860484Sobrien          inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
307960484Sobrien        }
308060484Sobrien      else
308160484Sobrien        inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
308260484Sobrien#ifndef TE_WINCE
308360484Sobrien      inst.reloc.exp.X_add_number -= 8;  /* PC rel adjust */
308460484Sobrien#endif
308560484Sobrien      inst.reloc.pc_rel = 1;
308660484Sobrien      inst.instruction |= (REG_PC << 16);
308760484Sobrien      pre_inc = 1;
308860484Sobrien    }
308960484Sobrien
309060484Sobrien  if (pre_inc && (flags & TRANS_BIT))
309160484Sobrien    inst.error = _("Pre-increment instruction with translate");
309260484Sobrien
309360484Sobrien  inst.instruction |= flags | (pre_inc ? PRE_INDEX : 0);
309460484Sobrien  end_of_line (str);
309560484Sobrien  return;
309660484Sobrien}
309760484Sobrien
309860484Sobrienstatic long
309960484Sobrienreg_list (strp)
310060484Sobrien     char ** strp;
310160484Sobrien{
310260484Sobrien  char * str = *strp;
310360484Sobrien  long   range = 0;
310460484Sobrien  int    another_range;
310560484Sobrien
310660484Sobrien  /* We come back here if we get ranges concatenated by '+' or '|' */
310760484Sobrien  do
310860484Sobrien    {
310960484Sobrien      another_range = 0;
311060484Sobrien
311160484Sobrien      if (*str == '{')
311260484Sobrien	{
311360484Sobrien	  int in_range = 0;
311460484Sobrien	  int cur_reg = -1;
311560484Sobrien
311660484Sobrien	  str++;
311760484Sobrien	  do
311860484Sobrien	    {
311960484Sobrien	      int reg;
312060484Sobrien
312160484Sobrien	      skip_whitespace (str);
312260484Sobrien
312360484Sobrien	      if ((reg = reg_required_here (& str, -1)) == FAIL)
312460484Sobrien		return FAIL;
312560484Sobrien
312660484Sobrien	      if (in_range)
312760484Sobrien		{
312860484Sobrien		  int i;
312960484Sobrien
313060484Sobrien		  if (reg <= cur_reg)
313160484Sobrien		    {
313260484Sobrien		      inst.error = _("Bad range in register list");
313360484Sobrien		      return FAIL;
313460484Sobrien		    }
313560484Sobrien
313660484Sobrien		  for (i = cur_reg + 1; i < reg; i++)
313760484Sobrien		    {
313860484Sobrien		      if (range & (1 << i))
313960484Sobrien			as_tsktsk
314060484Sobrien			  (_("Warning: Duplicated register (r%d) in register list"),
314160484Sobrien			   i);
314260484Sobrien		      else
314360484Sobrien			range |= 1 << i;
314460484Sobrien		    }
314560484Sobrien		  in_range = 0;
314660484Sobrien		}
314760484Sobrien
314860484Sobrien	      if (range & (1 << reg))
314960484Sobrien		as_tsktsk (_("Warning: Duplicated register (r%d) in register list"),
315060484Sobrien			   reg);
315160484Sobrien	      else if (reg <= cur_reg)
315260484Sobrien		as_tsktsk (_("Warning: Register range not in ascending order"));
315360484Sobrien
315460484Sobrien	      range |= 1 << reg;
315560484Sobrien	      cur_reg = reg;
315660484Sobrien	    } while (skip_past_comma (&str) != FAIL
315760484Sobrien		     || (in_range = 1, *str++ == '-'));
315860484Sobrien	  str--;
315960484Sobrien	  skip_whitespace (str);
316060484Sobrien
316160484Sobrien	  if (*str++ != '}')
316260484Sobrien	    {
316360484Sobrien	      inst.error = _("Missing `}'");
316460484Sobrien	      return FAIL;
316560484Sobrien	    }
316660484Sobrien	}
316760484Sobrien      else
316860484Sobrien	{
316960484Sobrien	  expressionS expr;
317060484Sobrien
317160484Sobrien	  if (my_get_expression (&expr, &str))
317260484Sobrien	    return FAIL;
317360484Sobrien
317460484Sobrien	  if (expr.X_op == O_constant)
317560484Sobrien	    {
317660484Sobrien	      if (expr.X_add_number
317760484Sobrien		  != (expr.X_add_number & 0x0000ffff))
317860484Sobrien		{
317960484Sobrien		  inst.error = _("invalid register mask");
318060484Sobrien		  return FAIL;
318160484Sobrien		}
318260484Sobrien
318360484Sobrien	      if ((range & expr.X_add_number) != 0)
318460484Sobrien		{
318560484Sobrien		  int regno = range & expr.X_add_number;
318660484Sobrien
318760484Sobrien		  regno &= -regno;
318860484Sobrien		  regno = (1 << regno) - 1;
318960484Sobrien		  as_tsktsk
319060484Sobrien		    (_("Warning: Duplicated register (r%d) in register list"),
319160484Sobrien		     regno);
319260484Sobrien		}
319360484Sobrien
319460484Sobrien	      range |= expr.X_add_number;
319560484Sobrien	    }
319660484Sobrien	  else
319760484Sobrien	    {
319860484Sobrien	      if (inst.reloc.type != 0)
319960484Sobrien		{
320060484Sobrien		  inst.error = _("expression too complex");
320160484Sobrien		  return FAIL;
320260484Sobrien		}
320360484Sobrien
320460484Sobrien	      memcpy (&inst.reloc.exp, &expr, sizeof (expressionS));
320560484Sobrien	      inst.reloc.type = BFD_RELOC_ARM_MULTI;
320660484Sobrien	      inst.reloc.pc_rel = 0;
320760484Sobrien	    }
320860484Sobrien	}
320960484Sobrien
321060484Sobrien      skip_whitespace (str);
321160484Sobrien
321260484Sobrien      if (*str == '|' || *str == '+')
321360484Sobrien	{
321460484Sobrien	  str++;
321560484Sobrien	  another_range = 1;
321660484Sobrien	}
321760484Sobrien    } while (another_range);
321860484Sobrien
321960484Sobrien  *strp = str;
322060484Sobrien  return range;
322160484Sobrien}
322260484Sobrien
322360484Sobrienstatic void
322460484Sobriendo_ldmstm (str, flags)
322560484Sobrien     char *        str;
322660484Sobrien     unsigned long flags;
322760484Sobrien{
322860484Sobrien  int base_reg;
322960484Sobrien  long range;
323060484Sobrien
323160484Sobrien  skip_whitespace (str);
323260484Sobrien
323360484Sobrien  if ((base_reg = reg_required_here (&str, 16)) == FAIL)
323460484Sobrien    return;
323560484Sobrien
323660484Sobrien  if (base_reg == REG_PC)
323760484Sobrien    {
323860484Sobrien      inst.error = _("r15 not allowed as base register");
323960484Sobrien      return;
324060484Sobrien    }
324160484Sobrien
324260484Sobrien  skip_whitespace (str);
324360484Sobrien
324460484Sobrien  if (*str == '!')
324560484Sobrien    {
324660484Sobrien      flags |= WRITE_BACK;
324760484Sobrien      str++;
324860484Sobrien    }
324960484Sobrien
325060484Sobrien  if (skip_past_comma (&str) == FAIL
325160484Sobrien      || (range = reg_list (&str)) == FAIL)
325260484Sobrien    {
325360484Sobrien      if (! inst.error)
325460484Sobrien	inst.error = BAD_ARGS;
325560484Sobrien      return;
325660484Sobrien    }
325760484Sobrien
325860484Sobrien  if (*str == '^')
325960484Sobrien    {
326060484Sobrien      str++;
326160484Sobrien      flags |= LDM_TYPE_2_OR_3;
326260484Sobrien    }
326360484Sobrien
326460484Sobrien  inst.instruction |= flags | range;
326560484Sobrien  end_of_line (str);
326660484Sobrien  return;
326760484Sobrien}
326860484Sobrien
326960484Sobrienstatic void
327060484Sobriendo_swi (str, flags)
327160484Sobrien     char *        str;
327260484Sobrien     unsigned long flags;
327360484Sobrien{
327460484Sobrien  skip_whitespace (str);
327560484Sobrien
327660484Sobrien  /* Allow optional leading '#'.  */
327760484Sobrien  if (is_immediate_prefix (*str))
327860484Sobrien    str++;
327960484Sobrien
328060484Sobrien  if (my_get_expression (& inst.reloc.exp, & str))
328160484Sobrien    return;
328260484Sobrien
328360484Sobrien  inst.reloc.type = BFD_RELOC_ARM_SWI;
328460484Sobrien  inst.reloc.pc_rel = 0;
328560484Sobrien  inst.instruction |= flags;
328660484Sobrien
328760484Sobrien  end_of_line (str);
328860484Sobrien
328960484Sobrien  return;
329060484Sobrien}
329160484Sobrien
329260484Sobrienstatic void
329360484Sobriendo_swap (str, flags)
329460484Sobrien     char *        str;
329560484Sobrien     unsigned long flags;
329660484Sobrien{
329760484Sobrien  int reg;
329860484Sobrien
329960484Sobrien  skip_whitespace (str);
330060484Sobrien
330160484Sobrien  if ((reg = reg_required_here (&str, 12)) == FAIL)
330260484Sobrien    return;
330360484Sobrien
330460484Sobrien  if (reg == REG_PC)
330560484Sobrien    {
330660484Sobrien      inst.error = _("r15 not allowed in swap");
330760484Sobrien      return;
330860484Sobrien    }
330960484Sobrien
331060484Sobrien  if (skip_past_comma (&str) == FAIL
331160484Sobrien      || (reg = reg_required_here (&str, 0)) == FAIL)
331260484Sobrien    {
331360484Sobrien      if (!inst.error)
331460484Sobrien	inst.error = BAD_ARGS;
331560484Sobrien      return;
331660484Sobrien    }
331760484Sobrien
331860484Sobrien  if (reg == REG_PC)
331960484Sobrien    {
332060484Sobrien      inst.error = _("r15 not allowed in swap");
332160484Sobrien      return;
332260484Sobrien    }
332360484Sobrien
332460484Sobrien  if (skip_past_comma (&str) == FAIL
332560484Sobrien      || *str++ != '[')
332660484Sobrien    {
332760484Sobrien      inst.error = BAD_ARGS;
332860484Sobrien      return;
332960484Sobrien    }
333060484Sobrien
333160484Sobrien  skip_whitespace (str);
333260484Sobrien
333360484Sobrien  if ((reg = reg_required_here (&str, 16)) == FAIL)
333460484Sobrien    return;
333560484Sobrien
333660484Sobrien  if (reg == REG_PC)
333760484Sobrien    {
333860484Sobrien      inst.error = BAD_PC;
333960484Sobrien      return;
334060484Sobrien    }
334160484Sobrien
334260484Sobrien  skip_whitespace (str);
334360484Sobrien
334460484Sobrien  if (*str++ != ']')
334560484Sobrien    {
334660484Sobrien      inst.error = _("missing ]");
334760484Sobrien      return;
334860484Sobrien    }
334960484Sobrien
335060484Sobrien  inst.instruction |= flags;
335160484Sobrien  end_of_line (str);
335260484Sobrien  return;
335360484Sobrien}
335460484Sobrien
335560484Sobrienstatic void
335660484Sobriendo_branch (str, flags)
335760484Sobrien     char *        str;
335860484Sobrien     unsigned long flags ATTRIBUTE_UNUSED;
335960484Sobrien{
336060484Sobrien  if (my_get_expression (&inst.reloc.exp, &str))
336160484Sobrien    return;
336260484Sobrien
336360484Sobrien#ifdef OBJ_ELF
336460484Sobrien  {
336560484Sobrien    char * save_in;
336660484Sobrien
336760484Sobrien    /* ScottB: February 5, 1998 */
336860484Sobrien    /* Check to see of PLT32 reloc required for the instruction.  */
336960484Sobrien
337060484Sobrien    /* arm_parse_reloc() works on input_line_pointer.
337160484Sobrien       We actually want to parse the operands to the branch instruction
337260484Sobrien       passed in 'str'.  Save the input pointer and restore it later.  */
337360484Sobrien    save_in = input_line_pointer;
337460484Sobrien    input_line_pointer = str;
337560484Sobrien    if (inst.reloc.exp.X_op == O_symbol
337660484Sobrien	&& *str == '('
337760484Sobrien	&& arm_parse_reloc () == BFD_RELOC_ARM_PLT32)
337860484Sobrien      {
337960484Sobrien	inst.reloc.type   = BFD_RELOC_ARM_PLT32;
338060484Sobrien	inst.reloc.pc_rel = 0;
338160484Sobrien	/* Modify str to point to after parsed operands, otherwise
338260484Sobrien	   end_of_line() will complain about the (PLT) left in str.  */
338360484Sobrien	str = input_line_pointer;
338460484Sobrien      }
338560484Sobrien    else
338660484Sobrien      {
338760484Sobrien	inst.reloc.type   = BFD_RELOC_ARM_PCREL_BRANCH;
338860484Sobrien	inst.reloc.pc_rel = 1;
338960484Sobrien      }
339060484Sobrien    input_line_pointer = save_in;
339160484Sobrien  }
339260484Sobrien#else
339360484Sobrien  inst.reloc.type   = BFD_RELOC_ARM_PCREL_BRANCH;
339460484Sobrien  inst.reloc.pc_rel = 1;
339560484Sobrien#endif /* OBJ_ELF */
339660484Sobrien
339760484Sobrien  end_of_line (str);
339860484Sobrien  return;
339960484Sobrien}
340060484Sobrien
340160484Sobrienstatic void
340260484Sobriendo_bx (str, flags)
340360484Sobrien     char *        str;
340460484Sobrien     unsigned long flags ATTRIBUTE_UNUSED;
340560484Sobrien{
340660484Sobrien  int reg;
340760484Sobrien
340860484Sobrien  skip_whitespace (str);
340960484Sobrien
341060484Sobrien  if ((reg = reg_required_here (&str, 0)) == FAIL)
341160484Sobrien    {
341260484Sobrien      inst.error = BAD_ARGS;
341360484Sobrien      return;
341460484Sobrien    }
341560484Sobrien
341660484Sobrien  if (reg == REG_PC)
341760484Sobrien    inst.error = BAD_PC;
341860484Sobrien
341960484Sobrien  end_of_line (str);
342060484Sobrien}
342160484Sobrien
342260484Sobrienstatic void
342360484Sobriendo_cdp (str, flags)
342460484Sobrien     char *        str;
342560484Sobrien     unsigned long flags ATTRIBUTE_UNUSED;
342660484Sobrien{
342760484Sobrien  /* Co-processor data operation.
342860484Sobrien     Format: CDP{cond} CP#,<expr>,CRd,CRn,CRm{,<expr>}  */
342960484Sobrien  skip_whitespace (str);
343060484Sobrien
343160484Sobrien  if (co_proc_number (&str) == FAIL)
343260484Sobrien    {
343360484Sobrien      if (!inst.error)
343460484Sobrien	inst.error = BAD_ARGS;
343560484Sobrien      return;
343660484Sobrien    }
343760484Sobrien
343860484Sobrien  if (skip_past_comma (&str) == FAIL
343960484Sobrien      || cp_opc_expr (&str, 20,4) == FAIL)
344060484Sobrien    {
344160484Sobrien      if (!inst.error)
344260484Sobrien	inst.error = BAD_ARGS;
344360484Sobrien      return;
344460484Sobrien    }
344560484Sobrien
344660484Sobrien  if (skip_past_comma (&str) == FAIL
344760484Sobrien      || cp_reg_required_here (&str, 12) == FAIL)
344860484Sobrien    {
344960484Sobrien      if (!inst.error)
345060484Sobrien	inst.error = BAD_ARGS;
345160484Sobrien      return;
345260484Sobrien    }
345360484Sobrien
345460484Sobrien  if (skip_past_comma (&str) == FAIL
345560484Sobrien      || cp_reg_required_here (&str, 16) == FAIL)
345660484Sobrien    {
345760484Sobrien      if (!inst.error)
345860484Sobrien	inst.error = BAD_ARGS;
345960484Sobrien      return;
346060484Sobrien    }
346160484Sobrien
346260484Sobrien  if (skip_past_comma (&str) == FAIL
346360484Sobrien      || cp_reg_required_here (&str, 0) == FAIL)
346460484Sobrien    {
346560484Sobrien      if (!inst.error)
346660484Sobrien	inst.error = BAD_ARGS;
346760484Sobrien      return;
346860484Sobrien    }
346960484Sobrien
347060484Sobrien  if (skip_past_comma (&str) == SUCCESS)
347160484Sobrien    {
347260484Sobrien      if (cp_opc_expr (&str, 5, 3) == FAIL)
347360484Sobrien	{
347460484Sobrien	  if (!inst.error)
347560484Sobrien	    inst.error = BAD_ARGS;
347660484Sobrien	  return;
347760484Sobrien	}
347860484Sobrien    }
347960484Sobrien
348060484Sobrien  end_of_line (str);
348160484Sobrien  return;
348260484Sobrien}
348360484Sobrien
348460484Sobrienstatic void
348560484Sobriendo_lstc (str, flags)
348660484Sobrien     char *        str;
348760484Sobrien     unsigned long flags;
348860484Sobrien{
348960484Sobrien  /* Co-processor register load/store.
349060484Sobrien     Format: <LDC|STC{cond}[L] CP#,CRd,<address>  */
349160484Sobrien
349260484Sobrien  skip_whitespace (str);
349360484Sobrien
349460484Sobrien  if (co_proc_number (&str) == FAIL)
349560484Sobrien    {
349660484Sobrien      if (!inst.error)
349760484Sobrien	inst.error = BAD_ARGS;
349860484Sobrien      return;
349960484Sobrien    }
350060484Sobrien
350160484Sobrien  if (skip_past_comma (&str) == FAIL
350260484Sobrien      || cp_reg_required_here (&str, 12) == FAIL)
350360484Sobrien    {
350460484Sobrien      if (!inst.error)
350560484Sobrien	inst.error = BAD_ARGS;
350660484Sobrien      return;
350760484Sobrien    }
350860484Sobrien
350960484Sobrien  if (skip_past_comma (&str) == FAIL
351060484Sobrien      || cp_address_required_here (&str) == FAIL)
351160484Sobrien    {
351260484Sobrien      if (! inst.error)
351360484Sobrien	inst.error = BAD_ARGS;
351460484Sobrien      return;
351560484Sobrien    }
351660484Sobrien
351760484Sobrien  inst.instruction |= flags;
351860484Sobrien  end_of_line (str);
351960484Sobrien  return;
352060484Sobrien}
352160484Sobrien
352260484Sobrienstatic void
352360484Sobriendo_co_reg (str, flags)
352460484Sobrien     char *        str;
352560484Sobrien     unsigned long flags;
352660484Sobrien{
352760484Sobrien  /* Co-processor register transfer.
352860484Sobrien     Format: <MCR|MRC>{cond} CP#,<expr1>,Rd,CRn,CRm{,<expr2>}  */
352960484Sobrien
353060484Sobrien  skip_whitespace (str);
353160484Sobrien
353260484Sobrien  if (co_proc_number (&str) == FAIL)
353360484Sobrien    {
353460484Sobrien      if (!inst.error)
353560484Sobrien	inst.error = BAD_ARGS;
353660484Sobrien      return;
353760484Sobrien    }
353860484Sobrien
353960484Sobrien  if (skip_past_comma (&str) == FAIL
354060484Sobrien      || cp_opc_expr (&str, 21, 3) == FAIL)
354160484Sobrien    {
354260484Sobrien      if (!inst.error)
354360484Sobrien	inst.error = BAD_ARGS;
354460484Sobrien      return;
354560484Sobrien    }
354660484Sobrien
354760484Sobrien  if (skip_past_comma (&str) == FAIL
354860484Sobrien      || reg_required_here (&str, 12) == FAIL)
354960484Sobrien    {
355060484Sobrien      if (!inst.error)
355160484Sobrien	inst.error = BAD_ARGS;
355260484Sobrien      return;
355360484Sobrien    }
355460484Sobrien
355560484Sobrien  if (skip_past_comma (&str) == FAIL
355660484Sobrien      || cp_reg_required_here (&str, 16) == FAIL)
355760484Sobrien    {
355860484Sobrien      if (!inst.error)
355960484Sobrien	inst.error = BAD_ARGS;
356060484Sobrien      return;
356160484Sobrien    }
356260484Sobrien
356360484Sobrien  if (skip_past_comma (&str) == FAIL
356460484Sobrien      || cp_reg_required_here (&str, 0) == FAIL)
356560484Sobrien    {
356660484Sobrien      if (!inst.error)
356760484Sobrien	inst.error = BAD_ARGS;
356860484Sobrien      return;
356960484Sobrien    }
357060484Sobrien
357160484Sobrien  if (skip_past_comma (&str) == SUCCESS)
357260484Sobrien    {
357360484Sobrien      if (cp_opc_expr (&str, 5, 3) == FAIL)
357460484Sobrien	{
357560484Sobrien	  if (!inst.error)
357660484Sobrien	    inst.error = BAD_ARGS;
357760484Sobrien	  return;
357860484Sobrien	}
357960484Sobrien    }
358060484Sobrien  if (flags)
358160484Sobrien    {
358260484Sobrien      inst.error = BAD_COND;
358360484Sobrien    }
358460484Sobrien
358560484Sobrien  end_of_line (str);
358660484Sobrien  return;
358760484Sobrien}
358860484Sobrien
358960484Sobrienstatic void
359060484Sobriendo_fp_ctrl (str, flags)
359160484Sobrien     char *        str;
359260484Sobrien     unsigned long flags ATTRIBUTE_UNUSED;
359360484Sobrien{
359460484Sobrien  /* FP control registers.
359560484Sobrien     Format: <WFS|RFS|WFC|RFC>{cond} Rn  */
359660484Sobrien
359760484Sobrien  skip_whitespace (str);
359860484Sobrien
359960484Sobrien  if (reg_required_here (&str, 12) == FAIL)
360060484Sobrien    {
360160484Sobrien      if (!inst.error)
360260484Sobrien	inst.error = BAD_ARGS;
360360484Sobrien      return;
360460484Sobrien    }
360560484Sobrien
360660484Sobrien  end_of_line (str);
360760484Sobrien  return;
360860484Sobrien}
360960484Sobrien
361060484Sobrienstatic void
361160484Sobriendo_fp_ldst (str, flags)
361260484Sobrien     char *        str;
361360484Sobrien     unsigned long flags ATTRIBUTE_UNUSED;
361460484Sobrien{
361560484Sobrien  skip_whitespace (str);
361660484Sobrien
361760484Sobrien  switch (inst.suffix)
361860484Sobrien    {
361960484Sobrien    case SUFF_S:
362060484Sobrien      break;
362160484Sobrien    case SUFF_D:
362260484Sobrien      inst.instruction |= CP_T_X;
362360484Sobrien      break;
362460484Sobrien    case SUFF_E:
362560484Sobrien      inst.instruction |= CP_T_Y;
362660484Sobrien      break;
362760484Sobrien    case SUFF_P:
362860484Sobrien      inst.instruction |= CP_T_X | CP_T_Y;
362960484Sobrien      break;
363060484Sobrien    default:
363160484Sobrien      abort ();
363260484Sobrien    }
363360484Sobrien
363460484Sobrien  if (fp_reg_required_here (&str, 12) == FAIL)
363560484Sobrien    {
363660484Sobrien      if (!inst.error)
363760484Sobrien	inst.error = BAD_ARGS;
363860484Sobrien      return;
363960484Sobrien    }
364060484Sobrien
364160484Sobrien  if (skip_past_comma (&str) == FAIL
364260484Sobrien      || cp_address_required_here (&str) == FAIL)
364360484Sobrien    {
364460484Sobrien      if (!inst.error)
364560484Sobrien	inst.error = BAD_ARGS;
364660484Sobrien      return;
364760484Sobrien    }
364860484Sobrien
364960484Sobrien  end_of_line (str);
365060484Sobrien}
365160484Sobrien
365260484Sobrienstatic void
365360484Sobriendo_fp_ldmstm (str, flags)
365460484Sobrien     char *        str;
365560484Sobrien     unsigned long flags;
365660484Sobrien{
365760484Sobrien  int num_regs;
365860484Sobrien
365960484Sobrien  skip_whitespace (str);
366060484Sobrien
366160484Sobrien  if (fp_reg_required_here (&str, 12) == FAIL)
366260484Sobrien    {
366360484Sobrien      if (! inst.error)
366460484Sobrien	inst.error = BAD_ARGS;
366560484Sobrien      return;
366660484Sobrien    }
366760484Sobrien
366860484Sobrien  /* Get Number of registers to transfer */
366960484Sobrien  if (skip_past_comma (&str) == FAIL
367060484Sobrien      || my_get_expression (&inst.reloc.exp, &str))
367160484Sobrien    {
367260484Sobrien      if (! inst.error)
367360484Sobrien	inst.error = _("constant expression expected");
367460484Sobrien      return;
367560484Sobrien    }
367660484Sobrien
367760484Sobrien  if (inst.reloc.exp.X_op != O_constant)
367860484Sobrien    {
367960484Sobrien      inst.error = _("Constant value required for number of registers");
368060484Sobrien      return;
368160484Sobrien    }
368260484Sobrien
368360484Sobrien  num_regs = inst.reloc.exp.X_add_number;
368460484Sobrien
368560484Sobrien  if (num_regs < 1 || num_regs > 4)
368660484Sobrien    {
368760484Sobrien      inst.error = _("number of registers must be in the range [1:4]");
368860484Sobrien      return;
368960484Sobrien    }
369060484Sobrien
369160484Sobrien  switch (num_regs)
369260484Sobrien    {
369360484Sobrien    case 1:
369460484Sobrien      inst.instruction |= CP_T_X;
369560484Sobrien      break;
369660484Sobrien    case 2:
369760484Sobrien      inst.instruction |= CP_T_Y;
369860484Sobrien      break;
369960484Sobrien    case 3:
370060484Sobrien      inst.instruction |= CP_T_Y | CP_T_X;
370160484Sobrien      break;
370260484Sobrien    case 4:
370360484Sobrien      break;
370460484Sobrien    default:
370560484Sobrien      abort ();
370660484Sobrien    }
370760484Sobrien
370860484Sobrien  if (flags)
370960484Sobrien    {
371060484Sobrien      int reg;
371160484Sobrien      int write_back;
371260484Sobrien      int offset;
371360484Sobrien
371460484Sobrien      /* The instruction specified "ea" or "fd", so we can only accept
371560484Sobrien	 [Rn]{!}.  The instruction does not really support stacking or
371660484Sobrien	 unstacking, so we have to emulate these by setting appropriate
371760484Sobrien	 bits and offsets.  */
371860484Sobrien      if (skip_past_comma (&str) == FAIL
371960484Sobrien	  || *str != '[')
372060484Sobrien	{
372160484Sobrien	  if (! inst.error)
372260484Sobrien	    inst.error = BAD_ARGS;
372360484Sobrien	  return;
372460484Sobrien	}
372560484Sobrien
372660484Sobrien      str++;
372760484Sobrien      skip_whitespace (str);
372860484Sobrien
372960484Sobrien      if ((reg = reg_required_here (&str, 16)) == FAIL)
373060484Sobrien	return;
373160484Sobrien
373260484Sobrien      skip_whitespace (str);
373360484Sobrien
373460484Sobrien      if (*str != ']')
373560484Sobrien	{
373660484Sobrien	  inst.error = BAD_ARGS;
373760484Sobrien	  return;
373860484Sobrien	}
373960484Sobrien
374060484Sobrien      str++;
374160484Sobrien      if (*str == '!')
374260484Sobrien	{
374360484Sobrien	  write_back = 1;
374460484Sobrien	  str++;
374560484Sobrien	  if (reg == REG_PC)
374660484Sobrien	    {
374760484Sobrien	      inst.error = _("R15 not allowed as base register with write-back");
374860484Sobrien	      return;
374960484Sobrien	    }
375060484Sobrien	}
375160484Sobrien      else
375260484Sobrien	write_back = 0;
375360484Sobrien
375460484Sobrien      if (flags & CP_T_Pre)
375560484Sobrien	{
375660484Sobrien	  /* Pre-decrement */
375760484Sobrien	  offset = 3 * num_regs;
375860484Sobrien	  if (write_back)
375960484Sobrien	    flags |= CP_T_WB;
376060484Sobrien	}
376160484Sobrien      else
376260484Sobrien	{
376360484Sobrien	  /* Post-increment */
376460484Sobrien	  if (write_back)
376560484Sobrien	    {
376660484Sobrien	      flags |= CP_T_WB;
376760484Sobrien	      offset = 3 * num_regs;
376860484Sobrien	    }
376960484Sobrien	  else
377060484Sobrien	    {
377160484Sobrien	      /* No write-back, so convert this into a standard pre-increment
377260484Sobrien		 instruction -- aesthetically more pleasing.  */
377360484Sobrien	      flags = CP_T_Pre | CP_T_UD;
377460484Sobrien	      offset = 0;
377560484Sobrien	    }
377660484Sobrien	}
377760484Sobrien
377860484Sobrien      inst.instruction |= flags | offset;
377960484Sobrien    }
378060484Sobrien  else if (skip_past_comma (&str) == FAIL
378160484Sobrien	   || cp_address_required_here (&str) == FAIL)
378260484Sobrien    {
378360484Sobrien      if (! inst.error)
378460484Sobrien	inst.error = BAD_ARGS;
378560484Sobrien      return;
378660484Sobrien    }
378760484Sobrien
378860484Sobrien  end_of_line (str);
378960484Sobrien}
379060484Sobrien
379160484Sobrienstatic void
379260484Sobriendo_fp_dyadic (str, flags)
379360484Sobrien     char *        str;
379460484Sobrien     unsigned long flags;
379560484Sobrien{
379660484Sobrien  skip_whitespace (str);
379760484Sobrien
379860484Sobrien  switch (inst.suffix)
379960484Sobrien    {
380060484Sobrien    case SUFF_S:
380160484Sobrien      break;
380260484Sobrien    case SUFF_D:
380360484Sobrien      inst.instruction |= 0x00000080;
380460484Sobrien      break;
380560484Sobrien    case SUFF_E:
380660484Sobrien      inst.instruction |= 0x00080000;
380760484Sobrien      break;
380860484Sobrien    default:
380960484Sobrien      abort ();
381060484Sobrien    }
381160484Sobrien
381260484Sobrien  if (fp_reg_required_here (&str, 12) == FAIL)
381360484Sobrien    {
381460484Sobrien      if (! inst.error)
381560484Sobrien	inst.error = BAD_ARGS;
381660484Sobrien      return;
381760484Sobrien    }
381860484Sobrien
381960484Sobrien  if (skip_past_comma (&str) == FAIL
382060484Sobrien      || fp_reg_required_here (&str, 16) == FAIL)
382160484Sobrien    {
382260484Sobrien      if (! inst.error)
382360484Sobrien	inst.error = BAD_ARGS;
382460484Sobrien      return;
382560484Sobrien    }
382660484Sobrien
382760484Sobrien  if (skip_past_comma (&str) == FAIL
382860484Sobrien      || fp_op2 (&str) == FAIL)
382960484Sobrien    {
383060484Sobrien      if (! inst.error)
383160484Sobrien	inst.error = BAD_ARGS;
383260484Sobrien      return;
383360484Sobrien    }
383460484Sobrien
383560484Sobrien  inst.instruction |= flags;
383660484Sobrien  end_of_line (str);
383760484Sobrien  return;
383860484Sobrien}
383960484Sobrien
384060484Sobrienstatic void
384160484Sobriendo_fp_monadic (str, flags)
384260484Sobrien     char *        str;
384360484Sobrien     unsigned long flags;
384460484Sobrien{
384560484Sobrien  skip_whitespace (str);
384660484Sobrien
384760484Sobrien  switch (inst.suffix)
384860484Sobrien    {
384960484Sobrien    case SUFF_S:
385060484Sobrien      break;
385160484Sobrien    case SUFF_D:
385260484Sobrien      inst.instruction |= 0x00000080;
385360484Sobrien      break;
385460484Sobrien    case SUFF_E:
385560484Sobrien      inst.instruction |= 0x00080000;
385660484Sobrien      break;
385760484Sobrien    default:
385860484Sobrien      abort ();
385960484Sobrien    }
386060484Sobrien
386160484Sobrien  if (fp_reg_required_here (&str, 12) == FAIL)
386260484Sobrien    {
386360484Sobrien      if (! inst.error)
386460484Sobrien	inst.error = BAD_ARGS;
386560484Sobrien      return;
386660484Sobrien    }
386760484Sobrien
386860484Sobrien  if (skip_past_comma (&str) == FAIL
386960484Sobrien      || fp_op2 (&str) == FAIL)
387060484Sobrien    {
387160484Sobrien      if (! inst.error)
387260484Sobrien	inst.error = BAD_ARGS;
387360484Sobrien      return;
387460484Sobrien    }
387560484Sobrien
387660484Sobrien  inst.instruction |= flags;
387760484Sobrien  end_of_line (str);
387860484Sobrien  return;
387960484Sobrien}
388060484Sobrien
388160484Sobrienstatic void
388260484Sobriendo_fp_cmp (str, flags)
388360484Sobrien     char *        str;
388460484Sobrien     unsigned long flags;
388560484Sobrien{
388660484Sobrien  skip_whitespace (str);
388760484Sobrien
388860484Sobrien  if (fp_reg_required_here (&str, 16) == FAIL)
388960484Sobrien    {
389060484Sobrien      if (! inst.error)
389160484Sobrien	inst.error = BAD_ARGS;
389260484Sobrien      return;
389360484Sobrien    }
389460484Sobrien
389560484Sobrien  if (skip_past_comma (&str) == FAIL
389660484Sobrien      || fp_op2 (&str) == FAIL)
389760484Sobrien    {
389860484Sobrien      if (! inst.error)
389960484Sobrien	inst.error = BAD_ARGS;
390060484Sobrien      return;
390160484Sobrien    }
390260484Sobrien
390360484Sobrien  inst.instruction |= flags;
390460484Sobrien  end_of_line (str);
390560484Sobrien  return;
390660484Sobrien}
390760484Sobrien
390860484Sobrienstatic void
390960484Sobriendo_fp_from_reg (str, flags)
391060484Sobrien     char *        str;
391160484Sobrien     unsigned long flags;
391260484Sobrien{
391360484Sobrien  skip_whitespace (str);
391460484Sobrien
391560484Sobrien  switch (inst.suffix)
391660484Sobrien    {
391760484Sobrien    case SUFF_S:
391860484Sobrien      break;
391960484Sobrien    case SUFF_D:
392060484Sobrien      inst.instruction |= 0x00000080;
392160484Sobrien      break;
392260484Sobrien    case SUFF_E:
392360484Sobrien      inst.instruction |= 0x00080000;
392460484Sobrien      break;
392560484Sobrien    default:
392660484Sobrien      abort ();
392760484Sobrien    }
392860484Sobrien
392960484Sobrien  if (fp_reg_required_here (&str, 16) == FAIL)
393060484Sobrien    {
393160484Sobrien      if (! inst.error)
393260484Sobrien	inst.error = BAD_ARGS;
393360484Sobrien      return;
393460484Sobrien    }
393560484Sobrien
393660484Sobrien  if (skip_past_comma (&str) == FAIL
393760484Sobrien      || reg_required_here (&str, 12) == FAIL)
393860484Sobrien    {
393960484Sobrien      if (! inst.error)
394060484Sobrien	inst.error = BAD_ARGS;
394160484Sobrien      return;
394260484Sobrien    }
394360484Sobrien
394460484Sobrien  inst.instruction |= flags;
394560484Sobrien  end_of_line (str);
394660484Sobrien  return;
394760484Sobrien}
394860484Sobrien
394960484Sobrienstatic void
395060484Sobriendo_fp_to_reg (str, flags)
395160484Sobrien     char *        str;
395260484Sobrien     unsigned long flags;
395360484Sobrien{
395460484Sobrien  skip_whitespace (str);
395560484Sobrien
395660484Sobrien  if (reg_required_here (&str, 12) == FAIL)
395760484Sobrien    return;
395860484Sobrien
395960484Sobrien  if (skip_past_comma (&str) == FAIL
396060484Sobrien      || fp_reg_required_here (&str, 0) == FAIL)
396160484Sobrien    {
396260484Sobrien      if (! inst.error)
396360484Sobrien	inst.error = BAD_ARGS;
396460484Sobrien      return;
396560484Sobrien    }
396660484Sobrien
396760484Sobrien  inst.instruction |= flags;
396860484Sobrien  end_of_line (str);
396960484Sobrien  return;
397060484Sobrien}
397160484Sobrien
397260484Sobrien/* Thumb specific routines */
397360484Sobrien
397460484Sobrien/* Parse and validate that a register is of the right form, this saves
397560484Sobrien   repeated checking of this information in many similar cases.
397660484Sobrien   Unlike the 32-bit case we do not insert the register into the opcode
397760484Sobrien   here, since the position is often unknown until the full instruction
397860484Sobrien   has been parsed.  */
397960484Sobrienstatic int
398060484Sobrienthumb_reg (strp, hi_lo)
398160484Sobrien     char ** strp;
398260484Sobrien     int     hi_lo;
398360484Sobrien{
398460484Sobrien  int reg;
398560484Sobrien
398660484Sobrien  if ((reg = reg_required_here (strp, -1)) == FAIL)
398760484Sobrien    return FAIL;
398860484Sobrien
398960484Sobrien  switch (hi_lo)
399060484Sobrien    {
399160484Sobrien    case THUMB_REG_LO:
399260484Sobrien      if (reg > 7)
399360484Sobrien	{
399460484Sobrien	  inst.error = _("lo register required");
399560484Sobrien	  return FAIL;
399660484Sobrien	}
399760484Sobrien      break;
399860484Sobrien
399960484Sobrien    case THUMB_REG_HI:
400060484Sobrien      if (reg < 8)
400160484Sobrien	{
400260484Sobrien	  inst.error = _("hi register required");
400360484Sobrien	  return FAIL;
400460484Sobrien	}
400560484Sobrien      break;
400660484Sobrien
400760484Sobrien    default:
400860484Sobrien      break;
400960484Sobrien    }
401060484Sobrien
401160484Sobrien  return reg;
401260484Sobrien}
401360484Sobrien
401460484Sobrien/* Parse an add or subtract instruction, SUBTRACT is non-zero if the opcode
401560484Sobrien   was SUB.  */
401660484Sobrienstatic void
401760484Sobrienthumb_add_sub (str, subtract)
401860484Sobrien     char * str;
401960484Sobrien     int    subtract;
402060484Sobrien{
402160484Sobrien  int Rd, Rs, Rn = FAIL;
402260484Sobrien
402360484Sobrien  skip_whitespace (str);
402460484Sobrien
402560484Sobrien  if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
402660484Sobrien      || skip_past_comma (&str) == FAIL)
402760484Sobrien    {
402860484Sobrien      if (! inst.error)
402960484Sobrien	inst.error = BAD_ARGS;
403060484Sobrien      return;
403160484Sobrien    }
403260484Sobrien
403360484Sobrien  if (is_immediate_prefix (*str))
403460484Sobrien    {
403560484Sobrien      Rs = Rd;
403660484Sobrien      str++;
403760484Sobrien      if (my_get_expression (&inst.reloc.exp, &str))
403860484Sobrien	return;
403960484Sobrien    }
404060484Sobrien  else
404160484Sobrien    {
404260484Sobrien      if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
404360484Sobrien	return;
404460484Sobrien
404560484Sobrien      if (skip_past_comma (&str) == FAIL)
404660484Sobrien	{
404760484Sobrien	  /* Two operand format, shuffle the registers and pretend there
404860484Sobrien	     are 3 */
404960484Sobrien	  Rn = Rs;
405060484Sobrien	  Rs = Rd;
405160484Sobrien	}
405260484Sobrien      else if (is_immediate_prefix (*str))
405360484Sobrien	{
405460484Sobrien	  str++;
405560484Sobrien	  if (my_get_expression (&inst.reloc.exp, &str))
405660484Sobrien	    return;
405760484Sobrien	}
405860484Sobrien      else if ((Rn = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
405960484Sobrien	return;
406060484Sobrien    }
406160484Sobrien
406260484Sobrien  /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
406360484Sobrien     for the latter case, EXPR contains the immediate that was found. */
406460484Sobrien  if (Rn != FAIL)
406560484Sobrien    {
406660484Sobrien      /* All register format.  */
406760484Sobrien      if (Rd > 7 || Rs > 7 || Rn > 7)
406860484Sobrien	{
406960484Sobrien	  if (Rs != Rd)
407060484Sobrien	    {
407160484Sobrien	      inst.error = _("dest and source1 must be the same register");
407260484Sobrien	      return;
407360484Sobrien	    }
407460484Sobrien
407560484Sobrien	  /* Can't do this for SUB */
407660484Sobrien	  if (subtract)
407760484Sobrien	    {
407860484Sobrien	      inst.error = _("subtract valid only on lo regs");
407960484Sobrien	      return;
408060484Sobrien	    }
408160484Sobrien
408260484Sobrien	  inst.instruction = (T_OPCODE_ADD_HI
408360484Sobrien			      | (Rd > 7 ? THUMB_H1 : 0)
408460484Sobrien			      | (Rn > 7 ? THUMB_H2 : 0));
408560484Sobrien	  inst.instruction |= (Rd & 7) | ((Rn & 7) << 3);
408660484Sobrien	}
408760484Sobrien      else
408860484Sobrien	{
408960484Sobrien	  inst.instruction = subtract ? T_OPCODE_SUB_R3 : T_OPCODE_ADD_R3;
409060484Sobrien	  inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
409160484Sobrien	}
409260484Sobrien    }
409360484Sobrien  else
409460484Sobrien    {
409560484Sobrien      /* Immediate expression, now things start to get nasty.  */
409660484Sobrien
409760484Sobrien      /* First deal with HI regs, only very restricted cases allowed:
409860484Sobrien	 Adjusting SP, and using PC or SP to get an address.  */
409960484Sobrien      if ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
410060484Sobrien	  || (Rs > 7 && Rs != REG_SP && Rs != REG_PC))
410160484Sobrien	{
410260484Sobrien	  inst.error = _("invalid Hi register with immediate");
410360484Sobrien	  return;
410460484Sobrien	}
410560484Sobrien
410660484Sobrien      if (inst.reloc.exp.X_op != O_constant)
410760484Sobrien	{
410860484Sobrien	  /* Value isn't known yet, all we can do is store all the fragments
410960484Sobrien	     we know about in the instruction and let the reloc hacking
411060484Sobrien	     work it all out.  */
411160484Sobrien	  inst.instruction = (subtract ? 0x8000 : 0) | (Rd << 4) | Rs;
411260484Sobrien	  inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
411360484Sobrien	}
411460484Sobrien      else
411560484Sobrien	{
411660484Sobrien	  int offset = inst.reloc.exp.X_add_number;
411760484Sobrien
411860484Sobrien	  if (subtract)
411960484Sobrien	    offset = -offset;
412060484Sobrien
412160484Sobrien	  if (offset < 0)
412260484Sobrien	    {
412360484Sobrien	      offset = -offset;
412460484Sobrien	      subtract = 1;
412560484Sobrien
412660484Sobrien	      /* Quick check, in case offset is MIN_INT */
412760484Sobrien	      if (offset < 0)
412860484Sobrien		{
412960484Sobrien		  inst.error = _("immediate value out of range");
413060484Sobrien		  return;
413160484Sobrien		}
413260484Sobrien	    }
413360484Sobrien	  else
413460484Sobrien	    subtract = 0;
413560484Sobrien
413660484Sobrien	  if (Rd == REG_SP)
413760484Sobrien	    {
413860484Sobrien	      if (offset & ~0x1fc)
413960484Sobrien		{
414060484Sobrien		  inst.error = _("invalid immediate value for stack adjust");
414160484Sobrien		  return;
414260484Sobrien		}
414360484Sobrien	      inst.instruction = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
414460484Sobrien	      inst.instruction |= offset >> 2;
414560484Sobrien	    }
414660484Sobrien	  else if (Rs == REG_PC || Rs == REG_SP)
414760484Sobrien	    {
414860484Sobrien	      if (subtract
414960484Sobrien		  || (offset & ~0x3fc))
415060484Sobrien		{
415160484Sobrien		  inst.error = _("invalid immediate for address calculation");
415260484Sobrien		  return;
415360484Sobrien		}
415460484Sobrien	      inst.instruction = (Rs == REG_PC ? T_OPCODE_ADD_PC
415560484Sobrien				  : T_OPCODE_ADD_SP);
415660484Sobrien	      inst.instruction |= (Rd << 8) | (offset >> 2);
415760484Sobrien	    }
415860484Sobrien	  else if (Rs == Rd)
415960484Sobrien	    {
416060484Sobrien	      if (offset & ~0xff)
416160484Sobrien		{
416260484Sobrien		  inst.error = _("immediate value out of range");
416360484Sobrien		  return;
416460484Sobrien		}
416560484Sobrien	      inst.instruction = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
416660484Sobrien	      inst.instruction |= (Rd << 8) | offset;
416760484Sobrien	    }
416860484Sobrien	  else
416960484Sobrien	    {
417060484Sobrien	      if (offset & ~0x7)
417160484Sobrien		{
417260484Sobrien		  inst.error = _("immediate value out of range");
417360484Sobrien		  return;
417460484Sobrien		}
417560484Sobrien	      inst.instruction = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
417660484Sobrien	      inst.instruction |= Rd | (Rs << 3) | (offset << 6);
417760484Sobrien	    }
417860484Sobrien	}
417960484Sobrien    }
418060484Sobrien
418160484Sobrien  end_of_line (str);
418260484Sobrien}
418360484Sobrien
418460484Sobrienstatic void
418560484Sobrienthumb_shift (str, shift)
418660484Sobrien     char * str;
418760484Sobrien     int    shift;
418860484Sobrien{
418960484Sobrien  int Rd, Rs, Rn = FAIL;
419060484Sobrien
419160484Sobrien  skip_whitespace (str);
419260484Sobrien
419360484Sobrien  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
419460484Sobrien      || skip_past_comma (&str) == FAIL)
419560484Sobrien    {
419660484Sobrien      if (! inst.error)
419760484Sobrien	inst.error = BAD_ARGS;
419860484Sobrien      return;
419960484Sobrien    }
420060484Sobrien
420160484Sobrien  if (is_immediate_prefix (*str))
420260484Sobrien    {
420360484Sobrien      /* Two operand immediate format, set Rs to Rd.  */
420460484Sobrien      Rs = Rd;
420560484Sobrien      str ++;
420660484Sobrien      if (my_get_expression (&inst.reloc.exp, &str))
420760484Sobrien	return;
420860484Sobrien    }
420960484Sobrien  else
421060484Sobrien    {
421160484Sobrien      if ((Rs =  thumb_reg (&str, THUMB_REG_LO)) == FAIL)
421260484Sobrien	return;
421360484Sobrien
421460484Sobrien      if (skip_past_comma (&str) == FAIL)
421560484Sobrien	{
421660484Sobrien	  /* Two operand format, shuffle the registers and pretend there
421760484Sobrien	     are 3 */
421860484Sobrien	  Rn = Rs;
421960484Sobrien	  Rs = Rd;
422060484Sobrien	}
422160484Sobrien      else if (is_immediate_prefix (*str))
422260484Sobrien	{
422360484Sobrien	  str++;
422460484Sobrien	  if (my_get_expression (&inst.reloc.exp, &str))
422560484Sobrien	    return;
422660484Sobrien	}
422760484Sobrien      else if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
422860484Sobrien	return;
422960484Sobrien    }
423060484Sobrien
423160484Sobrien  /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
423260484Sobrien     for the latter case, EXPR contains the immediate that was found. */
423360484Sobrien
423460484Sobrien  if (Rn != FAIL)
423560484Sobrien    {
423660484Sobrien      if (Rs != Rd)
423760484Sobrien	{
423860484Sobrien	  inst.error = _("source1 and dest must be same register");
423960484Sobrien	  return;
424060484Sobrien	}
424160484Sobrien
424260484Sobrien      switch (shift)
424360484Sobrien	{
424460484Sobrien	case THUMB_ASR: inst.instruction = T_OPCODE_ASR_R; break;
424560484Sobrien	case THUMB_LSL: inst.instruction = T_OPCODE_LSL_R; break;
424660484Sobrien	case THUMB_LSR: inst.instruction = T_OPCODE_LSR_R; break;
424760484Sobrien	}
424860484Sobrien
424960484Sobrien      inst.instruction |= Rd | (Rn << 3);
425060484Sobrien    }
425160484Sobrien  else
425260484Sobrien    {
425360484Sobrien      switch (shift)
425460484Sobrien	{
425560484Sobrien	case THUMB_ASR: inst.instruction = T_OPCODE_ASR_I; break;
425660484Sobrien	case THUMB_LSL: inst.instruction = T_OPCODE_LSL_I; break;
425760484Sobrien	case THUMB_LSR: inst.instruction = T_OPCODE_LSR_I; break;
425860484Sobrien	}
425960484Sobrien
426060484Sobrien      if (inst.reloc.exp.X_op != O_constant)
426160484Sobrien	{
426260484Sobrien	  /* Value isn't known yet, create a dummy reloc and let reloc
426360484Sobrien	     hacking fix it up */
426460484Sobrien
426560484Sobrien	  inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
426660484Sobrien	}
426760484Sobrien      else
426860484Sobrien	{
426960484Sobrien	  unsigned shift_value = inst.reloc.exp.X_add_number;
427060484Sobrien
427160484Sobrien	  if (shift_value > 32 || (shift_value == 32 && shift == THUMB_LSL))
427260484Sobrien	    {
427360484Sobrien	      inst.error = _("Invalid immediate for shift");
427460484Sobrien	      return;
427560484Sobrien	    }
427660484Sobrien
427760484Sobrien	  /* Shifts of zero are handled by converting to LSL */
427860484Sobrien	  if (shift_value == 0)
427960484Sobrien	    inst.instruction = T_OPCODE_LSL_I;
428060484Sobrien
428160484Sobrien	  /* Shifts of 32 are encoded as a shift of zero */
428260484Sobrien	  if (shift_value == 32)
428360484Sobrien	    shift_value = 0;
428460484Sobrien
428560484Sobrien	  inst.instruction |= shift_value << 6;
428660484Sobrien	}
428760484Sobrien
428860484Sobrien      inst.instruction |= Rd | (Rs << 3);
428960484Sobrien    }
429060484Sobrien
429160484Sobrien  end_of_line (str);
429260484Sobrien}
429360484Sobrien
429460484Sobrienstatic void
429560484Sobrienthumb_mov_compare (str, move)
429660484Sobrien     char * str;
429760484Sobrien     int    move;
429860484Sobrien{
429960484Sobrien  int Rd, Rs = FAIL;
430060484Sobrien
430160484Sobrien  skip_whitespace (str);
430260484Sobrien
430360484Sobrien  if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
430460484Sobrien      || skip_past_comma (&str) == FAIL)
430560484Sobrien    {
430660484Sobrien      if (! inst.error)
430760484Sobrien	inst.error = BAD_ARGS;
430860484Sobrien      return;
430960484Sobrien    }
431060484Sobrien
431160484Sobrien  if (is_immediate_prefix (*str))
431260484Sobrien    {
431360484Sobrien      str++;
431460484Sobrien      if (my_get_expression (&inst.reloc.exp, &str))
431560484Sobrien	return;
431660484Sobrien    }
431760484Sobrien  else if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
431860484Sobrien    return;
431960484Sobrien
432060484Sobrien  if (Rs != FAIL)
432160484Sobrien    {
432260484Sobrien      if (Rs < 8 && Rd < 8)
432360484Sobrien	{
432460484Sobrien	  if (move == THUMB_MOVE)
432560484Sobrien	    /* A move of two lowregs is encoded as ADD Rd, Rs, #0
432660484Sobrien	       since a MOV instruction produces unpredictable results */
432760484Sobrien	    inst.instruction = T_OPCODE_ADD_I3;
432860484Sobrien	  else
432960484Sobrien	    inst.instruction = T_OPCODE_CMP_LR;
433060484Sobrien	  inst.instruction |= Rd | (Rs << 3);
433160484Sobrien	}
433260484Sobrien      else
433360484Sobrien	{
433460484Sobrien	  if (move == THUMB_MOVE)
433560484Sobrien	    inst.instruction = T_OPCODE_MOV_HR;
433660484Sobrien	  else
433760484Sobrien	    inst.instruction = T_OPCODE_CMP_HR;
433860484Sobrien
433960484Sobrien	  if (Rd > 7)
434060484Sobrien	    inst.instruction |= THUMB_H1;
434160484Sobrien
434260484Sobrien	  if (Rs > 7)
434360484Sobrien	    inst.instruction |= THUMB_H2;
434460484Sobrien
434560484Sobrien	  inst.instruction |= (Rd & 7) | ((Rs & 7) << 3);
434660484Sobrien	}
434760484Sobrien    }
434860484Sobrien  else
434960484Sobrien    {
435060484Sobrien      if (Rd > 7)
435160484Sobrien	{
435260484Sobrien	  inst.error = _("only lo regs allowed with immediate");
435360484Sobrien	  return;
435460484Sobrien	}
435560484Sobrien
435660484Sobrien      if (move == THUMB_MOVE)
435760484Sobrien	inst.instruction = T_OPCODE_MOV_I8;
435860484Sobrien      else
435960484Sobrien	inst.instruction = T_OPCODE_CMP_I8;
436060484Sobrien
436160484Sobrien      inst.instruction |= Rd << 8;
436260484Sobrien
436360484Sobrien      if (inst.reloc.exp.X_op != O_constant)
436460484Sobrien	inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
436560484Sobrien      else
436660484Sobrien	{
436760484Sobrien	  unsigned value = inst.reloc.exp.X_add_number;
436860484Sobrien
436960484Sobrien	  if (value > 255)
437060484Sobrien	    {
437160484Sobrien	      inst.error = _("invalid immediate");
437260484Sobrien	      return;
437360484Sobrien	    }
437460484Sobrien
437560484Sobrien	  inst.instruction |= value;
437660484Sobrien	}
437760484Sobrien    }
437860484Sobrien
437960484Sobrien  end_of_line (str);
438060484Sobrien}
438160484Sobrien
438260484Sobrienstatic void
438360484Sobrienthumb_load_store (str, load_store, size)
438460484Sobrien     char * str;
438560484Sobrien     int    load_store;
438660484Sobrien     int    size;
438760484Sobrien{
438860484Sobrien  int Rd, Rb, Ro = FAIL;
438960484Sobrien
439060484Sobrien  skip_whitespace (str);
439160484Sobrien
439260484Sobrien  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
439360484Sobrien      || skip_past_comma (&str) == FAIL)
439460484Sobrien    {
439560484Sobrien      if (! inst.error)
439660484Sobrien	inst.error = BAD_ARGS;
439760484Sobrien      return;
439860484Sobrien    }
439960484Sobrien
440060484Sobrien  if (*str == '[')
440160484Sobrien    {
440260484Sobrien      str++;
440360484Sobrien      if ((Rb = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
440460484Sobrien	return;
440560484Sobrien
440660484Sobrien      if (skip_past_comma (&str) != FAIL)
440760484Sobrien	{
440860484Sobrien	  if (is_immediate_prefix (*str))
440960484Sobrien	    {
441060484Sobrien	      str++;
441160484Sobrien	      if (my_get_expression (&inst.reloc.exp, &str))
441260484Sobrien		return;
441360484Sobrien	    }
441460484Sobrien	  else if ((Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
441560484Sobrien	    return;
441660484Sobrien	}
441760484Sobrien      else
441860484Sobrien	{
441960484Sobrien	  inst.reloc.exp.X_op = O_constant;
442060484Sobrien	  inst.reloc.exp.X_add_number = 0;
442160484Sobrien	}
442260484Sobrien
442360484Sobrien      if (*str != ']')
442460484Sobrien	{
442560484Sobrien	  inst.error = _("expected ']'");
442660484Sobrien	  return;
442760484Sobrien	}
442860484Sobrien      str++;
442960484Sobrien    }
443060484Sobrien  else if (*str == '=')
443160484Sobrien    {
443260484Sobrien      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */
443360484Sobrien      str++;
443460484Sobrien
443560484Sobrien      skip_whitespace (str);
443660484Sobrien
443760484Sobrien      if (my_get_expression (& inst.reloc.exp, & str))
443860484Sobrien	return;
443960484Sobrien
444060484Sobrien      end_of_line (str);
444160484Sobrien
444260484Sobrien      if (   inst.reloc.exp.X_op != O_constant
444360484Sobrien	  && inst.reloc.exp.X_op != O_symbol)
444460484Sobrien	{
444560484Sobrien	  inst.error = "Constant expression expected";
444660484Sobrien	  return;
444760484Sobrien	}
444860484Sobrien
444960484Sobrien      if (inst.reloc.exp.X_op == O_constant
445060484Sobrien	  && ((inst.reloc.exp.X_add_number & ~0xFF) == 0))
445160484Sobrien	{
445260484Sobrien	  /* This can be done with a mov instruction */
445360484Sobrien
445460484Sobrien	  inst.instruction  = T_OPCODE_MOV_I8 | (Rd << 8);
445560484Sobrien	  inst.instruction |= inst.reloc.exp.X_add_number;
445660484Sobrien	  return;
445760484Sobrien	}
445860484Sobrien
445960484Sobrien      /* Insert into literal pool */
446060484Sobrien      if (add_to_lit_pool () == FAIL)
446160484Sobrien	{
446260484Sobrien	  if (!inst.error)
446360484Sobrien	    inst.error = "literal pool insertion failed";
446460484Sobrien	  return;
446560484Sobrien	}
446660484Sobrien
446760484Sobrien      inst.reloc.type   = BFD_RELOC_ARM_THUMB_OFFSET;
446860484Sobrien      inst.reloc.pc_rel = 1;
446960484Sobrien      inst.instruction  = T_OPCODE_LDR_PC | (Rd << 8);
447060484Sobrien      inst.reloc.exp.X_add_number += 4; /* Adjust ARM pipeline offset to Thumb */
447160484Sobrien
447260484Sobrien      return;
447360484Sobrien    }
447460484Sobrien  else
447560484Sobrien    {
447660484Sobrien      if (my_get_expression (&inst.reloc.exp, &str))
447760484Sobrien	return;
447860484Sobrien
447960484Sobrien      inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
448060484Sobrien      inst.reloc.pc_rel = 1;
448160484Sobrien      inst.reloc.exp.X_add_number -= 4; /* Pipeline offset */
448260484Sobrien      inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
448360484Sobrien      end_of_line (str);
448460484Sobrien      return;
448560484Sobrien    }
448660484Sobrien
448760484Sobrien  if (Rb == REG_PC || Rb == REG_SP)
448860484Sobrien    {
448960484Sobrien      if (size != THUMB_WORD)
449060484Sobrien	{
449160484Sobrien	  inst.error = _("byte or halfword not valid for base register");
449260484Sobrien	  return;
449360484Sobrien	}
449460484Sobrien      else if (Rb == REG_PC && load_store != THUMB_LOAD)
449560484Sobrien	{
449660484Sobrien	  inst.error = _("R15 based store not allowed");
449760484Sobrien	  return;
449860484Sobrien	}
449960484Sobrien      else if (Ro != FAIL)
450060484Sobrien	{
450160484Sobrien	  inst.error = _("Invalid base register for register offset");
450260484Sobrien	  return;
450360484Sobrien	}
450460484Sobrien
450560484Sobrien      if (Rb == REG_PC)
450660484Sobrien	inst.instruction = T_OPCODE_LDR_PC;
450760484Sobrien      else if (load_store == THUMB_LOAD)
450860484Sobrien	inst.instruction = T_OPCODE_LDR_SP;
450960484Sobrien      else
451060484Sobrien	inst.instruction = T_OPCODE_STR_SP;
451160484Sobrien
451260484Sobrien      inst.instruction |= Rd << 8;
451360484Sobrien      if (inst.reloc.exp.X_op == O_constant)
451460484Sobrien	{
451560484Sobrien	  unsigned offset = inst.reloc.exp.X_add_number;
451660484Sobrien
451760484Sobrien	  if (offset & ~0x3fc)
451860484Sobrien	    {
451960484Sobrien	      inst.error = _("invalid offset");
452060484Sobrien	      return;
452160484Sobrien	    }
452260484Sobrien
452360484Sobrien	  inst.instruction |= offset >> 2;
452460484Sobrien	}
452560484Sobrien      else
452660484Sobrien	inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
452760484Sobrien    }
452860484Sobrien  else if (Rb > 7)
452960484Sobrien    {
453060484Sobrien      inst.error = _("invalid base register in load/store");
453160484Sobrien      return;
453260484Sobrien    }
453360484Sobrien  else if (Ro == FAIL)
453460484Sobrien    {
453560484Sobrien      /* Immediate offset */
453660484Sobrien      if (size == THUMB_WORD)
453760484Sobrien	inst.instruction = (load_store == THUMB_LOAD
453860484Sobrien			    ? T_OPCODE_LDR_IW : T_OPCODE_STR_IW);
453960484Sobrien      else if (size == THUMB_HALFWORD)
454060484Sobrien	inst.instruction = (load_store == THUMB_LOAD
454160484Sobrien			    ? T_OPCODE_LDR_IH : T_OPCODE_STR_IH);
454260484Sobrien      else
454360484Sobrien	inst.instruction = (load_store == THUMB_LOAD
454460484Sobrien			    ? T_OPCODE_LDR_IB : T_OPCODE_STR_IB);
454560484Sobrien
454660484Sobrien      inst.instruction |= Rd | (Rb << 3);
454760484Sobrien
454860484Sobrien      if (inst.reloc.exp.X_op == O_constant)
454960484Sobrien	{
455060484Sobrien	  unsigned offset = inst.reloc.exp.X_add_number;
455160484Sobrien
455260484Sobrien	  if (offset & ~(0x1f << size))
455360484Sobrien	    {
455460484Sobrien	      inst.error = _("Invalid offset");
455560484Sobrien	      return;
455660484Sobrien	    }
455760484Sobrien	  inst.instruction |= (offset >> size) << 6;
455860484Sobrien	}
455960484Sobrien      else
456060484Sobrien	inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
456160484Sobrien    }
456260484Sobrien  else
456360484Sobrien    {
456460484Sobrien      /* Register offset */
456560484Sobrien      if (size == THUMB_WORD)
456660484Sobrien	inst.instruction = (load_store == THUMB_LOAD
456760484Sobrien			    ? T_OPCODE_LDR_RW : T_OPCODE_STR_RW);
456860484Sobrien      else if (size == THUMB_HALFWORD)
456960484Sobrien	inst.instruction = (load_store == THUMB_LOAD
457060484Sobrien			    ? T_OPCODE_LDR_RH : T_OPCODE_STR_RH);
457160484Sobrien      else
457260484Sobrien	inst.instruction = (load_store == THUMB_LOAD
457360484Sobrien			    ? T_OPCODE_LDR_RB : T_OPCODE_STR_RB);
457460484Sobrien
457560484Sobrien      inst.instruction |= Rd | (Rb << 3) | (Ro << 6);
457660484Sobrien    }
457760484Sobrien
457860484Sobrien  end_of_line (str);
457960484Sobrien}
458060484Sobrien
458160484Sobrienstatic void
458260484Sobriendo_t_nop (str)
458360484Sobrien     char * str;
458460484Sobrien{
458560484Sobrien  /* Do nothing */
458660484Sobrien  end_of_line (str);
458760484Sobrien  return;
458860484Sobrien}
458960484Sobrien
459060484Sobrien/* Handle the Format 4 instructions that do not have equivalents in other
459160484Sobrien   formats.  That is, ADC, AND, EOR, SBC, ROR, TST, NEG, CMN, ORR, MUL,
459260484Sobrien   BIC and MVN.  */
459360484Sobrienstatic void
459460484Sobriendo_t_arit (str)
459560484Sobrien     char * str;
459660484Sobrien{
459760484Sobrien  int Rd, Rs, Rn;
459860484Sobrien
459960484Sobrien  skip_whitespace (str);
460060484Sobrien
460160484Sobrien  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
460260484Sobrien      || skip_past_comma (&str) == FAIL
460360484Sobrien      || (Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
460460484Sobrien    {
460560484Sobrien    	inst.error = BAD_ARGS;
460660484Sobrien      	return;
460760484Sobrien    }
460860484Sobrien
460960484Sobrien  if (skip_past_comma (&str) != FAIL)
461060484Sobrien    {
461160484Sobrien      /* Three operand format not allowed for TST, CMN, NEG and MVN.
461260484Sobrien	 (It isn't allowed for CMP either, but that isn't handled by this
461360484Sobrien	 function.)  */
461460484Sobrien      if (inst.instruction == T_OPCODE_TST
461560484Sobrien	  || inst.instruction == T_OPCODE_CMN
461660484Sobrien	  || inst.instruction == T_OPCODE_NEG
461760484Sobrien 	  || inst.instruction == T_OPCODE_MVN)
461860484Sobrien	{
461960484Sobrien	  inst.error = BAD_ARGS;
462060484Sobrien	  return;
462160484Sobrien	}
462260484Sobrien
462360484Sobrien      if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
462460484Sobrien	return;
462560484Sobrien
462660484Sobrien      if (Rs != Rd)
462760484Sobrien	{
462860484Sobrien	  inst.error = _("dest and source1 one must be the same register");
462960484Sobrien	  return;
463060484Sobrien	}
463160484Sobrien      Rs = Rn;
463260484Sobrien    }
463360484Sobrien
463460484Sobrien  if (inst.instruction == T_OPCODE_MUL
463560484Sobrien      && Rs == Rd)
463660484Sobrien    as_tsktsk (_("Rs and Rd must be different in MUL"));
463760484Sobrien
463860484Sobrien  inst.instruction |= Rd | (Rs << 3);
463960484Sobrien  end_of_line (str);
464060484Sobrien}
464160484Sobrien
464260484Sobrienstatic void
464360484Sobriendo_t_add (str)
464460484Sobrien     char * str;
464560484Sobrien{
464660484Sobrien  thumb_add_sub (str, 0);
464760484Sobrien}
464860484Sobrien
464960484Sobrienstatic void
465060484Sobriendo_t_asr (str)
465160484Sobrien     char * str;
465260484Sobrien{
465360484Sobrien  thumb_shift (str, THUMB_ASR);
465460484Sobrien}
465560484Sobrien
465660484Sobrienstatic void
465760484Sobriendo_t_branch9 (str)
465860484Sobrien     char * str;
465960484Sobrien{
466060484Sobrien  if (my_get_expression (&inst.reloc.exp, &str))
466160484Sobrien    return;
466260484Sobrien  inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
466360484Sobrien  inst.reloc.pc_rel = 1;
466460484Sobrien  end_of_line (str);
466560484Sobrien}
466660484Sobrien
466760484Sobrienstatic void
466860484Sobriendo_t_branch12 (str)
466960484Sobrien     char * str;
467060484Sobrien{
467160484Sobrien  if (my_get_expression (&inst.reloc.exp, &str))
467260484Sobrien    return;
467360484Sobrien  inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
467460484Sobrien  inst.reloc.pc_rel = 1;
467560484Sobrien  end_of_line (str);
467660484Sobrien}
467760484Sobrien
467860484Sobrien/* Find the real, Thumb encoded start of a Thumb function.  */
467960484Sobrien
468060484Sobrienstatic symbolS *
468160484Sobrienfind_real_start (symbolP)
468260484Sobrien     symbolS * symbolP;
468360484Sobrien{
468460484Sobrien  char *       real_start;
468560484Sobrien  const char * name = S_GET_NAME (symbolP);
468660484Sobrien  symbolS *    new_target;
468760484Sobrien
468860484Sobrien  /* This definiton must agree with the one in gcc/config/arm/thumb.c */
468960484Sobrien#define STUB_NAME ".real_start_of"
469060484Sobrien
469160484Sobrien  if (name == NULL)
469260484Sobrien    abort();
469360484Sobrien
469460484Sobrien  /* Names that start with '.' are local labels, not function entry points.
469560484Sobrien     The compiler may generate BL instructions to these labels because it
469660484Sobrien     needs to perform a branch to a far away location.  */
469760484Sobrien  if (name[0] == '.')
469860484Sobrien    return symbolP;
469960484Sobrien
470060484Sobrien  real_start = malloc (strlen (name) + strlen (STUB_NAME) + 1);
470160484Sobrien  sprintf (real_start, "%s%s", STUB_NAME, name);
470260484Sobrien
470360484Sobrien  new_target = symbol_find (real_start);
470460484Sobrien
470560484Sobrien  if (new_target == NULL)
470660484Sobrien    {
470760484Sobrien      as_warn ("Failed to find real start of function: %s\n", name);
470860484Sobrien      new_target = symbolP;
470960484Sobrien    }
471060484Sobrien
471160484Sobrien  free (real_start);
471260484Sobrien
471360484Sobrien  return new_target;
471460484Sobrien}
471560484Sobrien
471660484Sobrien
471760484Sobrienstatic void
471860484Sobriendo_t_branch23 (str)
471960484Sobrien     char * str;
472060484Sobrien{
472160484Sobrien  if (my_get_expression (& inst.reloc.exp, & str))
472260484Sobrien    return;
472360484Sobrien
472460484Sobrien  inst.reloc.type   = BFD_RELOC_THUMB_PCREL_BRANCH23;
472560484Sobrien  inst.reloc.pc_rel = 1;
472660484Sobrien  end_of_line (str);
472760484Sobrien
472860484Sobrien  /* If the destination of the branch is a defined symbol which does not have
472960484Sobrien     the THUMB_FUNC attribute, then we must be calling a function which has
473060484Sobrien     the (interfacearm) attribute.  We look for the Thumb entry point to that
473160484Sobrien     function and change the branch to refer to that function instead.  */
473260484Sobrien  if (   inst.reloc.exp.X_op == O_symbol
473360484Sobrien      && inst.reloc.exp.X_add_symbol != NULL
473460484Sobrien      && S_IS_DEFINED (inst.reloc.exp.X_add_symbol)
473560484Sobrien      && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol))
473660484Sobrien    inst.reloc.exp.X_add_symbol = find_real_start (inst.reloc.exp.X_add_symbol);
473760484Sobrien}
473860484Sobrien
473960484Sobrienstatic void
474060484Sobriendo_t_bx (str)
474160484Sobrien     char * str;
474260484Sobrien{
474360484Sobrien  int reg;
474460484Sobrien
474560484Sobrien  skip_whitespace (str);
474660484Sobrien
474760484Sobrien  if ((reg = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
474860484Sobrien    return;
474960484Sobrien
475060484Sobrien  /* This sets THUMB_H2 from the top bit of reg.  */
475160484Sobrien  inst.instruction |= reg << 3;
475260484Sobrien
475360484Sobrien  /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC.  The reloc
475460484Sobrien     should cause the alignment to be checked once it is known.  This is
475560484Sobrien     because BX PC only works if the instruction is word aligned.  */
475660484Sobrien
475760484Sobrien  end_of_line (str);
475860484Sobrien}
475960484Sobrien
476060484Sobrienstatic void
476160484Sobriendo_t_compare (str)
476260484Sobrien     char * str;
476360484Sobrien{
476460484Sobrien  thumb_mov_compare (str, THUMB_COMPARE);
476560484Sobrien}
476660484Sobrien
476760484Sobrienstatic void
476860484Sobriendo_t_ldmstm (str)
476960484Sobrien     char * str;
477060484Sobrien{
477160484Sobrien  int Rb;
477260484Sobrien  long range;
477360484Sobrien
477460484Sobrien  skip_whitespace (str);
477560484Sobrien
477660484Sobrien  if ((Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
477760484Sobrien    return;
477860484Sobrien
477960484Sobrien  if (*str != '!')
478060484Sobrien    as_warn (_("Inserted missing '!': load/store multiple always writes back base register"));
478160484Sobrien  else
478260484Sobrien    str++;
478360484Sobrien
478460484Sobrien  if (skip_past_comma (&str) == FAIL
478560484Sobrien      || (range = reg_list (&str)) == FAIL)
478660484Sobrien    {
478760484Sobrien      if (! inst.error)
478860484Sobrien	inst.error = BAD_ARGS;
478960484Sobrien      return;
479060484Sobrien    }
479160484Sobrien
479260484Sobrien  if (inst.reloc.type != BFD_RELOC_NONE)
479360484Sobrien    {
479460484Sobrien      /* This really doesn't seem worth it. */
479560484Sobrien      inst.reloc.type = BFD_RELOC_NONE;
479660484Sobrien      inst.error = _("Expression too complex");
479760484Sobrien      return;
479860484Sobrien    }
479960484Sobrien
480060484Sobrien  if (range & ~0xff)
480160484Sobrien    {
480260484Sobrien      inst.error = _("only lo-regs valid in load/store multiple");
480360484Sobrien      return;
480460484Sobrien    }
480560484Sobrien
480660484Sobrien  inst.instruction |= (Rb << 8) | range;
480760484Sobrien  end_of_line (str);
480860484Sobrien}
480960484Sobrien
481060484Sobrienstatic void
481160484Sobriendo_t_ldr (str)
481260484Sobrien     char * str;
481360484Sobrien{
481460484Sobrien  thumb_load_store (str, THUMB_LOAD, THUMB_WORD);
481560484Sobrien}
481660484Sobrien
481760484Sobrienstatic void
481860484Sobriendo_t_ldrb (str)
481960484Sobrien     char * str;
482060484Sobrien{
482160484Sobrien  thumb_load_store (str, THUMB_LOAD, THUMB_BYTE);
482260484Sobrien}
482360484Sobrien
482460484Sobrienstatic void
482560484Sobriendo_t_ldrh (str)
482660484Sobrien     char * str;
482760484Sobrien{
482860484Sobrien  thumb_load_store (str, THUMB_LOAD, THUMB_HALFWORD);
482960484Sobrien}
483060484Sobrien
483160484Sobrienstatic void
483260484Sobriendo_t_lds (str)
483360484Sobrien     char * str;
483460484Sobrien{
483560484Sobrien  int Rd, Rb, Ro;
483660484Sobrien
483760484Sobrien  skip_whitespace (str);
483860484Sobrien
483960484Sobrien  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
484060484Sobrien      || skip_past_comma (&str) == FAIL
484160484Sobrien      || *str++ != '['
484260484Sobrien      || (Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL
484360484Sobrien      || skip_past_comma (&str) == FAIL
484460484Sobrien      || (Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL
484560484Sobrien      || *str++ != ']')
484660484Sobrien    {
484760484Sobrien      if (! inst.error)
484860484Sobrien	inst.error = _("Syntax: ldrs[b] Rd, [Rb, Ro]");
484960484Sobrien      return;
485060484Sobrien    }
485160484Sobrien
485260484Sobrien  inst.instruction |= Rd | (Rb << 3) | (Ro << 6);
485360484Sobrien  end_of_line (str);
485460484Sobrien}
485560484Sobrien
485660484Sobrienstatic void
485760484Sobriendo_t_lsl (str)
485860484Sobrien     char * str;
485960484Sobrien{
486060484Sobrien  thumb_shift (str, THUMB_LSL);
486160484Sobrien}
486260484Sobrien
486360484Sobrienstatic void
486460484Sobriendo_t_lsr (str)
486560484Sobrien     char * str;
486660484Sobrien{
486760484Sobrien  thumb_shift (str, THUMB_LSR);
486860484Sobrien}
486960484Sobrien
487060484Sobrienstatic void
487160484Sobriendo_t_mov (str)
487260484Sobrien     char * str;
487360484Sobrien{
487460484Sobrien  thumb_mov_compare (str, THUMB_MOVE);
487560484Sobrien}
487660484Sobrien
487760484Sobrienstatic void
487860484Sobriendo_t_push_pop (str)
487960484Sobrien     char * str;
488060484Sobrien{
488160484Sobrien  long range;
488260484Sobrien
488360484Sobrien  skip_whitespace (str);
488460484Sobrien
488560484Sobrien  if ((range = reg_list (&str)) == FAIL)
488660484Sobrien    {
488760484Sobrien      if (! inst.error)
488860484Sobrien	inst.error = BAD_ARGS;
488960484Sobrien      return;
489060484Sobrien    }
489160484Sobrien
489260484Sobrien  if (inst.reloc.type != BFD_RELOC_NONE)
489360484Sobrien    {
489460484Sobrien      /* This really doesn't seem worth it. */
489560484Sobrien      inst.reloc.type = BFD_RELOC_NONE;
489660484Sobrien      inst.error = _("Expression too complex");
489760484Sobrien      return;
489860484Sobrien    }
489960484Sobrien
490060484Sobrien  if (range & ~0xff)
490160484Sobrien    {
490260484Sobrien      if ((inst.instruction == T_OPCODE_PUSH
490360484Sobrien	   && (range & ~0xff) == 1 << REG_LR)
490460484Sobrien	  || (inst.instruction == T_OPCODE_POP
490560484Sobrien	      && (range & ~0xff) == 1 << REG_PC))
490660484Sobrien	{
490760484Sobrien	  inst.instruction |= THUMB_PP_PC_LR;
490860484Sobrien	  range &= 0xff;
490960484Sobrien	}
491060484Sobrien      else
491160484Sobrien	{
491260484Sobrien	  inst.error = _("invalid register list to push/pop instruction");
491360484Sobrien	  return;
491460484Sobrien	}
491560484Sobrien    }
491660484Sobrien
491760484Sobrien  inst.instruction |= range;
491860484Sobrien  end_of_line (str);
491960484Sobrien}
492060484Sobrien
492160484Sobrienstatic void
492260484Sobriendo_t_str (str)
492360484Sobrien     char * str;
492460484Sobrien{
492560484Sobrien  thumb_load_store (str, THUMB_STORE, THUMB_WORD);
492660484Sobrien}
492760484Sobrien
492860484Sobrienstatic void
492960484Sobriendo_t_strb (str)
493060484Sobrien     char * str;
493160484Sobrien{
493260484Sobrien  thumb_load_store (str, THUMB_STORE, THUMB_BYTE);
493360484Sobrien}
493460484Sobrien
493560484Sobrienstatic void
493660484Sobriendo_t_strh (str)
493760484Sobrien     char * str;
493860484Sobrien{
493960484Sobrien  thumb_load_store (str, THUMB_STORE, THUMB_HALFWORD);
494060484Sobrien}
494160484Sobrien
494260484Sobrienstatic void
494360484Sobriendo_t_sub (str)
494460484Sobrien     char * str;
494560484Sobrien{
494660484Sobrien  thumb_add_sub (str, 1);
494760484Sobrien}
494860484Sobrien
494960484Sobrienstatic void
495060484Sobriendo_t_swi (str)
495160484Sobrien     char * str;
495260484Sobrien{
495360484Sobrien  skip_whitespace (str);
495460484Sobrien
495560484Sobrien  if (my_get_expression (&inst.reloc.exp, &str))
495660484Sobrien    return;
495760484Sobrien
495860484Sobrien  inst.reloc.type = BFD_RELOC_ARM_SWI;
495960484Sobrien  end_of_line (str);
496060484Sobrien  return;
496160484Sobrien}
496260484Sobrien
496360484Sobrienstatic void
496460484Sobriendo_t_adr (str)
496560484Sobrien     char * str;
496660484Sobrien{
496760484Sobrien  int reg;
496860484Sobrien
496960484Sobrien  /* This is a pseudo-op of the form "adr rd, label" to be converted
497060484Sobrien     into a relative address of the form "add rd, pc, #label-.-4".  */
497160484Sobrien  skip_whitespace (str);
497260484Sobrien
497360484Sobrien  /* Store Rd in temporary location inside instruction.  */
497460484Sobrien  if ((reg = reg_required_here (&str, 4)) == FAIL
497560484Sobrien      || (reg > 7)  /* For Thumb reg must be r0..r7.  */
497660484Sobrien      || skip_past_comma (&str) == FAIL
497760484Sobrien      || my_get_expression (&inst.reloc.exp, &str))
497860484Sobrien    {
497960484Sobrien      if (!inst.error)
498060484Sobrien	inst.error = BAD_ARGS;
498160484Sobrien      return;
498260484Sobrien    }
498360484Sobrien
498460484Sobrien  inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
498560484Sobrien  inst.reloc.exp.X_add_number -= 4; /* PC relative adjust.  */
498660484Sobrien  inst.reloc.pc_rel = 1;
498760484Sobrien  inst.instruction |= REG_PC; /* Rd is already placed into the instruction.  */
498860484Sobrien
498960484Sobrien  end_of_line (str);
499060484Sobrien}
499160484Sobrien
499260484Sobrienstatic void
499360484Sobrieninsert_reg (entry)
499460484Sobrien     int entry;
499560484Sobrien{
499660484Sobrien  int    len = strlen (reg_table[entry].name) + 2;
499760484Sobrien  char * buf = (char *) xmalloc (len);
499860484Sobrien  char * buf2 = (char *) xmalloc (len);
499960484Sobrien  int    i = 0;
500060484Sobrien
500160484Sobrien#ifdef REGISTER_PREFIX
500260484Sobrien  buf[i++] = REGISTER_PREFIX;
500360484Sobrien#endif
500460484Sobrien
500560484Sobrien  strcpy (buf + i, reg_table[entry].name);
500660484Sobrien
500760484Sobrien  for (i = 0; buf[i]; i++)
500860484Sobrien    buf2[i] = islower (buf[i]) ? toupper (buf[i]) : buf[i];
500960484Sobrien
501060484Sobrien  buf2[i] = '\0';
501160484Sobrien
501260484Sobrien  hash_insert (arm_reg_hsh, buf, (PTR) &reg_table[entry]);
501360484Sobrien  hash_insert (arm_reg_hsh, buf2, (PTR) &reg_table[entry]);
501460484Sobrien}
501560484Sobrien
501660484Sobrienstatic void
501760484Sobrieninsert_reg_alias (str, regnum)
501860484Sobrien     char *str;
501960484Sobrien     int regnum;
502060484Sobrien{
502160484Sobrien  struct reg_entry *new =
502260484Sobrien    (struct reg_entry *)xmalloc (sizeof (struct reg_entry));
502360484Sobrien  char *name = xmalloc (strlen (str) + 1);
502460484Sobrien  strcpy (name, str);
502560484Sobrien
502660484Sobrien  new->name = name;
502760484Sobrien  new->number = regnum;
502860484Sobrien
502960484Sobrien  hash_insert (arm_reg_hsh, name, (PTR) new);
503060484Sobrien}
503160484Sobrien
503260484Sobrienstatic void
503360484Sobrienset_constant_flonums ()
503460484Sobrien{
503560484Sobrien  int i;
503660484Sobrien
503760484Sobrien  for (i = 0; i < NUM_FLOAT_VALS; i++)
503860484Sobrien    if (atof_ieee ((char *)fp_const[i], 'x', fp_values[i]) == NULL)
503960484Sobrien      abort ();
504060484Sobrien}
504160484Sobrien
504260484Sobrienvoid
504360484Sobrienmd_begin ()
504460484Sobrien{
504560484Sobrien  unsigned mach;
504660484Sobrien  unsigned int i;
504760484Sobrien
504860484Sobrien  if (   (arm_ops_hsh = hash_new ()) == NULL
504960484Sobrien      || (arm_tops_hsh = hash_new ()) == NULL
505060484Sobrien      || (arm_cond_hsh = hash_new ()) == NULL
505160484Sobrien      || (arm_shift_hsh = hash_new ()) == NULL
505260484Sobrien      || (arm_reg_hsh = hash_new ()) == NULL
505360484Sobrien      || (arm_psr_hsh = hash_new ()) == NULL)
505460484Sobrien    as_fatal (_("Virtual memory exhausted"));
505560484Sobrien
505660484Sobrien  for (i = 0; i < sizeof (insns) / sizeof (struct asm_opcode); i++)
505760484Sobrien    hash_insert (arm_ops_hsh, insns[i].template, (PTR) (insns + i));
505860484Sobrien  for (i = 0; i < sizeof (tinsns) / sizeof (struct thumb_opcode); i++)
505960484Sobrien    hash_insert (arm_tops_hsh, tinsns[i].template, (PTR) (tinsns + i));
506060484Sobrien  for (i = 0; i < sizeof (conds) / sizeof (struct asm_cond); i++)
506160484Sobrien    hash_insert (arm_cond_hsh, conds[i].template, (PTR) (conds + i));
506260484Sobrien  for (i = 0; i < sizeof (shift) / sizeof (struct asm_shift); i++)
506360484Sobrien    hash_insert (arm_shift_hsh, shift[i].template, (PTR) (shift + i));
506460484Sobrien  for (i = 0; i < sizeof (psrs) / sizeof (struct asm_psr); i++)
506560484Sobrien    hash_insert (arm_psr_hsh, psrs[i].template, (PTR) (psrs + i));
506660484Sobrien
506760484Sobrien  for (i = 0; reg_table[i].name; i++)
506860484Sobrien    insert_reg (i);
506960484Sobrien
507060484Sobrien  set_constant_flonums ();
507160484Sobrien
507260484Sobrien#if defined OBJ_COFF || defined OBJ_ELF
507360484Sobrien  {
507460484Sobrien    unsigned int flags = 0;
507560484Sobrien
507660484Sobrien    /* Set the flags in the private structure.  */
507760484Sobrien    if (uses_apcs_26)      flags |= F_APCS26;
507860484Sobrien    if (support_interwork) flags |= F_INTERWORK;
507960484Sobrien    if (uses_apcs_float)   flags |= F_APCS_FLOAT;
508060484Sobrien    if (pic_code)          flags |= F_PIC;
508160484Sobrien    if ((cpu_variant & FPU_ALL) == FPU_NONE) flags |= F_SOFT_FLOAT;
508260484Sobrien
508360484Sobrien    bfd_set_private_flags (stdoutput, flags);
508460484Sobrien  }
508560484Sobrien#endif
508660484Sobrien
508760484Sobrien  /* Record the CPU type as well.  */
508860484Sobrien  switch (cpu_variant & ARM_CPU_MASK)
508960484Sobrien    {
509060484Sobrien    case ARM_2:
509160484Sobrien      mach = bfd_mach_arm_2;
509260484Sobrien      break;
509360484Sobrien
509460484Sobrien    case ARM_3: 		/* Also ARM_250.  */
509560484Sobrien      mach = bfd_mach_arm_2a;
509660484Sobrien      break;
509760484Sobrien
509860484Sobrien    default:
509960484Sobrien    case ARM_6 | ARM_3 | ARM_2:	/* Actually no CPU type defined.  */
510060484Sobrien      mach = bfd_mach_arm_4;
510160484Sobrien      break;
510260484Sobrien
510360484Sobrien    case ARM_7: 		/* Also ARM_6.  */
510460484Sobrien      mach = bfd_mach_arm_3;
510560484Sobrien      break;
510660484Sobrien    }
510760484Sobrien
510860484Sobrien  /* Catch special cases.  */
510960484Sobrien  if (cpu_variant != (FPU_DEFAULT | CPU_DEFAULT))
511060484Sobrien    {
511160484Sobrien      if (cpu_variant & (ARM_EXT_V5 & ARM_THUMB))
511260484Sobrien	mach = bfd_mach_arm_5T;
511360484Sobrien      else if (cpu_variant & ARM_EXT_V5)
511460484Sobrien	mach = bfd_mach_arm_5;
511560484Sobrien      else if (cpu_variant & ARM_THUMB)
511660484Sobrien	mach = bfd_mach_arm_4T;
511760484Sobrien      else if ((cpu_variant & ARM_ARCH_V4) == ARM_ARCH_V4)
511860484Sobrien	mach = bfd_mach_arm_4;
511960484Sobrien      else if (cpu_variant & ARM_LONGMUL)
512060484Sobrien	mach = bfd_mach_arm_3M;
512160484Sobrien    }
512260484Sobrien
512360484Sobrien  bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
512460484Sobrien}
512560484Sobrien
512660484Sobrien/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
512760484Sobrien   for use in the a.out file, and stores them in the array pointed to by buf.
512860484Sobrien   This knows about the endian-ness of the target machine and does
512960484Sobrien   THE RIGHT THING, whatever it is.  Possible values for n are 1 (byte)
513060484Sobrien   2 (short) and 4 (long)  Floating numbers are put out as a series of
513160484Sobrien   LITTLENUMS (shorts, here at least).  */
513260484Sobrienvoid
513360484Sobrienmd_number_to_chars (buf, val, n)
513460484Sobrien     char * buf;
513560484Sobrien     valueT val;
513660484Sobrien     int    n;
513760484Sobrien{
513860484Sobrien  if (target_big_endian)
513960484Sobrien    number_to_chars_bigendian (buf, val, n);
514060484Sobrien  else
514160484Sobrien    number_to_chars_littleendian (buf, val, n);
514260484Sobrien}
514360484Sobrien
514460484Sobrienstatic valueT
514560484Sobrienmd_chars_to_number (buf, n)
514660484Sobrien     char * buf;
514760484Sobrien     int n;
514860484Sobrien{
514960484Sobrien  valueT result = 0;
515060484Sobrien  unsigned char * where = (unsigned char *) buf;
515160484Sobrien
515260484Sobrien  if (target_big_endian)
515360484Sobrien    {
515460484Sobrien      while (n--)
515560484Sobrien	{
515660484Sobrien	  result <<= 8;
515760484Sobrien	  result |= (*where++ & 255);
515860484Sobrien	}
515960484Sobrien    }
516060484Sobrien  else
516160484Sobrien    {
516260484Sobrien      while (n--)
516360484Sobrien	{
516460484Sobrien	  result <<= 8;
516560484Sobrien	  result |= (where[n] & 255);
516660484Sobrien	}
516760484Sobrien    }
516860484Sobrien
516960484Sobrien  return result;
517060484Sobrien}
517160484Sobrien
517260484Sobrien/* Turn a string in input_line_pointer into a floating point constant
517360484Sobrien   of type TYPE, and store the appropriate bytes in *litP.  The number
517460484Sobrien   of LITTLENUMS emitted is stored in *sizeP .  An error message is
517560484Sobrien   returned, or NULL on OK.
517660484Sobrien
517760484Sobrien   Note that fp constants aren't represent in the normal way on the ARM.
517860484Sobrien   In big endian mode, things are as expected.  However, in little endian
517960484Sobrien   mode fp constants are big-endian word-wise, and little-endian byte-wise
518060484Sobrien   within the words.  For example, (double) 1.1 in big endian mode is
518160484Sobrien   the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is
518260484Sobrien   the byte sequence 99 99 f1 3f 9a 99 99 99.
518360484Sobrien
518460484Sobrien   ??? The format of 12 byte floats is uncertain according to gcc's arm.h.  */
518560484Sobrien
518660484Sobrienchar *
518760484Sobrienmd_atof (type, litP, sizeP)
518860484Sobrien     char   type;
518960484Sobrien     char * litP;
519060484Sobrien     int *  sizeP;
519160484Sobrien{
519260484Sobrien  int prec;
519360484Sobrien  LITTLENUM_TYPE words[MAX_LITTLENUMS];
519460484Sobrien  char *t;
519560484Sobrien  int i;
519660484Sobrien
519760484Sobrien  switch (type)
519860484Sobrien    {
519960484Sobrien    case 'f':
520060484Sobrien    case 'F':
520160484Sobrien    case 's':
520260484Sobrien    case 'S':
520360484Sobrien      prec = 2;
520460484Sobrien      break;
520560484Sobrien
520660484Sobrien    case 'd':
520760484Sobrien    case 'D':
520860484Sobrien    case 'r':
520960484Sobrien    case 'R':
521060484Sobrien      prec = 4;
521160484Sobrien      break;
521260484Sobrien
521360484Sobrien    case 'x':
521460484Sobrien    case 'X':
521560484Sobrien      prec = 6;
521660484Sobrien      break;
521760484Sobrien
521860484Sobrien    case 'p':
521960484Sobrien    case 'P':
522060484Sobrien      prec = 6;
522160484Sobrien      break;
522260484Sobrien
522360484Sobrien    default:
522460484Sobrien      *sizeP = 0;
522560484Sobrien      return _("Bad call to MD_ATOF()");
522660484Sobrien    }
522760484Sobrien
522860484Sobrien  t = atof_ieee (input_line_pointer, type, words);
522960484Sobrien  if (t)
523060484Sobrien    input_line_pointer = t;
523160484Sobrien  *sizeP = prec * 2;
523260484Sobrien
523360484Sobrien  if (target_big_endian)
523460484Sobrien    {
523560484Sobrien      for (i = 0; i < prec; i++)
523660484Sobrien	{
523760484Sobrien	  md_number_to_chars (litP, (valueT) words[i], 2);
523860484Sobrien	  litP += 2;
523960484Sobrien	}
524060484Sobrien    }
524160484Sobrien  else
524260484Sobrien    {
524360484Sobrien      /* For a 4 byte float the order of elements in `words' is 1 0.  For an
524460484Sobrien	 8 byte float the order is 1 0 3 2.  */
524560484Sobrien      for (i = 0; i < prec; i += 2)
524660484Sobrien	{
524760484Sobrien	  md_number_to_chars (litP, (valueT) words[i + 1], 2);
524860484Sobrien	  md_number_to_chars (litP + 2, (valueT) words[i], 2);
524960484Sobrien	  litP += 4;
525060484Sobrien	}
525160484Sobrien    }
525260484Sobrien
525360484Sobrien  return 0;
525460484Sobrien}
525560484Sobrien
525660484Sobrien/* The knowledge of the PC's pipeline offset is built into the insns themselves.  */
525760484Sobrienlong
525860484Sobrienmd_pcrel_from (fixP)
525960484Sobrien     fixS * fixP;
526060484Sobrien{
526160484Sobrien  if (   fixP->fx_addsy
526260484Sobrien      && S_GET_SEGMENT (fixP->fx_addsy) == undefined_section
526360484Sobrien      && fixP->fx_subsy == NULL)
526460484Sobrien    return 0;
526560484Sobrien
526660484Sobrien  if (fixP->fx_pcrel && (fixP->fx_r_type == BFD_RELOC_ARM_THUMB_ADD))
526760484Sobrien    {
526860484Sobrien      /* PC relative addressing on the Thumb is slightly odd
526960484Sobrien	 as the bottom two bits of the PC are forced to zero
527060484Sobrien	 for the calculation.  */
527160484Sobrien      return (fixP->fx_where + fixP->fx_frag->fr_address) & ~3;
527260484Sobrien    }
527360484Sobrien
527460484Sobrien#ifdef TE_WINCE
527560484Sobrien  /* The pattern was adjusted to accomodate CE's off-by-one fixups,
527660484Sobrien     so we un-adjust here to compensate for the accomodation.  */
527760484Sobrien  return fixP->fx_where + fixP->fx_frag->fr_address + 8;
527860484Sobrien#else
527960484Sobrien  return fixP->fx_where + fixP->fx_frag->fr_address;
528060484Sobrien#endif
528160484Sobrien}
528260484Sobrien
528360484Sobrien/* Round up a section size to the appropriate boundary. */
528460484SobrienvalueT
528560484Sobrienmd_section_align (segment, size)
528660484Sobrien     segT   segment ATTRIBUTE_UNUSED;
528760484Sobrien     valueT size;
528860484Sobrien{
528960484Sobrien#ifdef OBJ_ELF
529060484Sobrien  return size;
529160484Sobrien#else
529260484Sobrien  /* Round all sects to multiple of 4 */
529360484Sobrien  return (size + 3) & ~3;
529460484Sobrien#endif
529560484Sobrien}
529660484Sobrien
529760484Sobrien/* Under ELF we need to default _GLOBAL_OFFSET_TABLE.  Otherwise
529860484Sobrien   we have no need to default values of symbols.  */
529960484Sobrien
530060484Sobrien/* ARGSUSED */
530160484SobriensymbolS *
530260484Sobrienmd_undefined_symbol (name)
530361843Sobrien     char * name ATTRIBUTE_UNUSED;
530460484Sobrien{
530560484Sobrien#ifdef OBJ_ELF
530660484Sobrien  if (name[0] == '_' && name[1] == 'G'
530760484Sobrien      && streq (name, GLOBAL_OFFSET_TABLE_NAME))
530860484Sobrien    {
530960484Sobrien      if (!GOT_symbol)
531060484Sobrien	{
531160484Sobrien	  if (symbol_find (name))
531260484Sobrien	    as_bad ("GOT already in the symbol table");
531360484Sobrien
531460484Sobrien	  GOT_symbol = symbol_new (name, undefined_section,
531560484Sobrien				   (valueT)0, & zero_address_frag);
531660484Sobrien	}
531760484Sobrien
531860484Sobrien      return GOT_symbol;
531960484Sobrien    }
532060484Sobrien#endif
532160484Sobrien
532260484Sobrien  return 0;
532360484Sobrien}
532460484Sobrien
532560484Sobrien/* arm_reg_parse () := if it looks like a register, return its token and
532660484Sobrien   advance the pointer. */
532760484Sobrien
532860484Sobrienstatic int
532960484Sobrienarm_reg_parse (ccp)
533060484Sobrien     register char ** ccp;
533160484Sobrien{
533260484Sobrien  char * start = * ccp;
533360484Sobrien  char   c;
533460484Sobrien  char * p;
533560484Sobrien  struct reg_entry * reg;
533660484Sobrien
533760484Sobrien#ifdef REGISTER_PREFIX
533860484Sobrien  if (*start != REGISTER_PREFIX)
533960484Sobrien    return FAIL;
534060484Sobrien  p = start + 1;
534160484Sobrien#else
534260484Sobrien  p = start;
534360484Sobrien#ifdef OPTIONAL_REGISTER_PREFIX
534460484Sobrien  if (*p == OPTIONAL_REGISTER_PREFIX)
534560484Sobrien    p++, start++;
534660484Sobrien#endif
534760484Sobrien#endif
534860484Sobrien  if (!isalpha (*p) || !is_name_beginner (*p))
534960484Sobrien    return FAIL;
535060484Sobrien
535160484Sobrien  c = *p++;
535260484Sobrien  while (isalpha (c) || isdigit (c) || c == '_')
535360484Sobrien    c = *p++;
535460484Sobrien
535560484Sobrien  *--p = 0;
535660484Sobrien  reg = (struct reg_entry *) hash_find (arm_reg_hsh, start);
535760484Sobrien  *p = c;
535860484Sobrien
535960484Sobrien  if (reg)
536060484Sobrien    {
536160484Sobrien      *ccp = p;
536260484Sobrien      return reg->number;
536360484Sobrien    }
536460484Sobrien
536560484Sobrien  return FAIL;
536660484Sobrien}
536760484Sobrien
536860484Sobrienint
536960484Sobrienmd_apply_fix3 (fixP, val, seg)
537060484Sobrien     fixS *      fixP;
537160484Sobrien     valueT *    val;
537260484Sobrien     segT        seg;
537360484Sobrien{
537460484Sobrien  offsetT        value = * val;
537560484Sobrien  offsetT        newval;
537660484Sobrien  unsigned int   newimm;
537760484Sobrien  unsigned long  temp;
537860484Sobrien  int            sign;
537960484Sobrien  char *         buf = fixP->fx_where + fixP->fx_frag->fr_literal;
538060484Sobrien  arm_fix_data * arm_data = (arm_fix_data *) fixP->tc_fix_data;
538160484Sobrien
538260484Sobrien  assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
538360484Sobrien
538460484Sobrien  /* Note whether this will delete the relocation.  */
538560484Sobrien#if 0 /* patch from REarnshaw to JDavis (disabled for the moment, since it doesn't work fully) */
538660484Sobrien  if ((fixP->fx_addsy == 0 || symbol_constant_p (fixP->fx_addsy))
538760484Sobrien      && !fixP->fx_pcrel)
538860484Sobrien#else
538960484Sobrien  if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
539060484Sobrien#endif
539160484Sobrien    fixP->fx_done = 1;
539260484Sobrien
539360484Sobrien  /* If this symbol is in a different section then we need to leave it for
539460484Sobrien     the linker to deal with.  Unfortunately, md_pcrel_from can't tell,
539560484Sobrien     so we have to undo it's effects here.  */
539660484Sobrien  if (fixP->fx_pcrel)
539760484Sobrien    {
539860484Sobrien      if (fixP->fx_addsy != NULL
539960484Sobrien	  && S_IS_DEFINED (fixP->fx_addsy)
540060484Sobrien	  && S_GET_SEGMENT (fixP->fx_addsy) != seg)
540160484Sobrien	{
540260484Sobrien	  if (target_oabi
540360484Sobrien	      && (fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH
540460484Sobrien		))
540560484Sobrien	    value = 0;
540660484Sobrien	  else
540760484Sobrien	    value += md_pcrel_from (fixP);
540860484Sobrien	}
540960484Sobrien    }
541060484Sobrien
541160484Sobrien  fixP->fx_addnumber = value;	/* Remember value for emit_reloc.  */
541260484Sobrien
541360484Sobrien  switch (fixP->fx_r_type)
541460484Sobrien    {
541560484Sobrien    case BFD_RELOC_ARM_IMMEDIATE:
541660484Sobrien      newimm = validate_immediate (value);
541760484Sobrien      temp = md_chars_to_number (buf, INSN_SIZE);
541860484Sobrien
541960484Sobrien      /* If the instruction will fail, see if we can fix things up by
542060484Sobrien	 changing the opcode.  */
542160484Sobrien      if (newimm == (unsigned int) FAIL
542260484Sobrien	  && (newimm = negate_data_op (&temp, value)) == (unsigned int) FAIL)
542360484Sobrien	{
542460484Sobrien	  as_bad_where (fixP->fx_file, fixP->fx_line,
542560484Sobrien			_("invalid constant (%lx) after fixup"),
542660484Sobrien			(unsigned long) value);
542760484Sobrien	  break;
542860484Sobrien	}
542960484Sobrien
543060484Sobrien      newimm |= (temp & 0xfffff000);
543160484Sobrien      md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
543260484Sobrien      break;
543360484Sobrien
543460484Sobrien    case BFD_RELOC_ARM_ADRL_IMMEDIATE:
543560484Sobrien      {
543660484Sobrien	unsigned int highpart = 0;
543760484Sobrien	unsigned int newinsn  = 0xe1a00000; /* nop */
543860484Sobrien	newimm = validate_immediate (value);
543960484Sobrien	temp = md_chars_to_number (buf, INSN_SIZE);
544060484Sobrien
544160484Sobrien	/* If the instruction will fail, see if we can fix things up by
544260484Sobrien	   changing the opcode.  */
544360484Sobrien	if (newimm == (unsigned int) FAIL
544460484Sobrien	    && (newimm = negate_data_op (& temp, value)) == (unsigned int) FAIL)
544560484Sobrien	  {
544660484Sobrien	    /* No ?  OK - try using two ADD instructions to generate the value.  */
544760484Sobrien	    newimm = validate_immediate_twopart (value, & highpart);
544860484Sobrien
544960484Sobrien	    /* Yes - then make sure that the second instruction is also an add.  */
545060484Sobrien	    if (newimm != (unsigned int) FAIL)
545160484Sobrien	      newinsn = temp;
545260484Sobrien	    /* Still No ?  Try using a negated value.  */
545368765Sobrien	    else if ((newimm = validate_immediate_twopart (- value, & highpart)) != (unsigned int) FAIL)
545460484Sobrien		temp = newinsn = (temp & OPCODE_MASK) | OPCODE_SUB << DATA_OP_SHIFT;
545560484Sobrien	    /* Otherwise - give up.  */
545660484Sobrien	    else
545760484Sobrien	      {
545860484Sobrien		as_bad_where (fixP->fx_file, fixP->fx_line,
545960484Sobrien			      _("Unable to compute ADRL instructions for PC offset of 0x%x"), value);
546060484Sobrien		break;
546160484Sobrien	      }
546260484Sobrien
546360484Sobrien	    /* Replace the first operand in the 2nd instruction (which is the PC)
546460484Sobrien	       with the destination register.  We have already added in the PC in the
546560484Sobrien	       first instruction and we do not want to do it again.  */
546660484Sobrien	    newinsn &= ~ 0xf0000;
546760484Sobrien	    newinsn |= ((newinsn & 0x0f000) << 4);
546860484Sobrien	  }
546960484Sobrien
547060484Sobrien	newimm |= (temp & 0xfffff000);
547160484Sobrien	md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
547260484Sobrien
547360484Sobrien	highpart |= (newinsn & 0xfffff000);
547460484Sobrien	md_number_to_chars (buf + INSN_SIZE, (valueT) highpart, INSN_SIZE);
547560484Sobrien      }
547660484Sobrien      break;
547760484Sobrien
547860484Sobrien    case BFD_RELOC_ARM_OFFSET_IMM:
547960484Sobrien      sign = value >= 0;
548060484Sobrien
548160484Sobrien      if (value < 0)
548260484Sobrien	value = - value;
548360484Sobrien
548460484Sobrien      if (validate_offset_imm (value, 0) == FAIL)
548560484Sobrien        {
548660484Sobrien	  as_bad_where (fixP->fx_file, fixP->fx_line,
548760484Sobrien                        _("bad immediate value for offset (%ld)"), (long) value);
548860484Sobrien          break;
548960484Sobrien        }
549060484Sobrien
549160484Sobrien      newval = md_chars_to_number (buf, INSN_SIZE);
549260484Sobrien      newval &= 0xff7ff000;
549360484Sobrien      newval |= value | (sign ? INDEX_UP : 0);
549460484Sobrien      md_number_to_chars (buf, newval, INSN_SIZE);
549560484Sobrien      break;
549660484Sobrien
549760484Sobrien     case BFD_RELOC_ARM_OFFSET_IMM8:
549860484Sobrien     case BFD_RELOC_ARM_HWLITERAL:
549960484Sobrien      sign = value >= 0;
550060484Sobrien
550160484Sobrien      if (value < 0)
550260484Sobrien	value = - value;
550360484Sobrien
550460484Sobrien      if (validate_offset_imm (value, 1) == FAIL)
550560484Sobrien        {
550660484Sobrien          if (fixP->fx_r_type == BFD_RELOC_ARM_HWLITERAL)
550760484Sobrien	    as_bad_where (fixP->fx_file, fixP->fx_line,
550860484Sobrien			_("invalid literal constant: pool needs to be closer"));
550960484Sobrien          else
551060484Sobrien            as_bad (_("bad immediate value for half-word offset (%ld)"),
551160484Sobrien		    (long) value);
551260484Sobrien          break;
551360484Sobrien        }
551460484Sobrien
551560484Sobrien      newval = md_chars_to_number (buf, INSN_SIZE);
551660484Sobrien      newval &= 0xff7ff0f0;
551760484Sobrien      newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0);
551860484Sobrien      md_number_to_chars (buf, newval, INSN_SIZE);
551960484Sobrien      break;
552060484Sobrien
552160484Sobrien    case BFD_RELOC_ARM_LITERAL:
552260484Sobrien      sign = value >= 0;
552360484Sobrien
552460484Sobrien      if (value < 0)
552560484Sobrien	value = - value;
552660484Sobrien
552760484Sobrien      if (validate_offset_imm (value, 0) == FAIL)
552860484Sobrien	{
552960484Sobrien	  as_bad_where (fixP->fx_file, fixP->fx_line,
553060484Sobrien			_("invalid literal constant: pool needs to be closer"));
553160484Sobrien	  break;
553260484Sobrien	}
553360484Sobrien
553460484Sobrien      newval = md_chars_to_number (buf, INSN_SIZE);
553560484Sobrien      newval &= 0xff7ff000;
553660484Sobrien      newval |= value | (sign ? INDEX_UP : 0);
553760484Sobrien      md_number_to_chars (buf, newval, INSN_SIZE);
553860484Sobrien      break;
553960484Sobrien
554060484Sobrien    case BFD_RELOC_ARM_SHIFT_IMM:
554160484Sobrien      newval = md_chars_to_number (buf, INSN_SIZE);
554260484Sobrien      if (((unsigned long) value) > 32
554360484Sobrien	  || (value == 32
554460484Sobrien	      && (((newval & 0x60) == 0) || (newval & 0x60) == 0x60)))
554560484Sobrien	{
554660484Sobrien	  as_bad_where (fixP->fx_file, fixP->fx_line,
554760484Sobrien			_("shift expression is too large"));
554860484Sobrien	  break;
554960484Sobrien	}
555060484Sobrien
555160484Sobrien      if (value == 0)
555260484Sobrien	newval &= ~0x60;	/* Shifts of zero must be done as lsl */
555360484Sobrien      else if (value == 32)
555460484Sobrien	value = 0;
555560484Sobrien      newval &= 0xfffff07f;
555660484Sobrien      newval |= (value & 0x1f) << 7;
555760484Sobrien      md_number_to_chars (buf, newval , INSN_SIZE);
555860484Sobrien      break;
555960484Sobrien
556060484Sobrien    case BFD_RELOC_ARM_SWI:
556160484Sobrien      if (arm_data->thumb_mode)
556260484Sobrien	{
556360484Sobrien	  if (((unsigned long) value) > 0xff)
556460484Sobrien	    as_bad_where (fixP->fx_file, fixP->fx_line,
556560484Sobrien			  _("Invalid swi expression"));
556660484Sobrien	  newval = md_chars_to_number (buf, THUMB_SIZE) & 0xff00;
556760484Sobrien	  newval |= value;
556860484Sobrien	  md_number_to_chars (buf, newval, THUMB_SIZE);
556960484Sobrien	}
557060484Sobrien      else
557160484Sobrien	{
557260484Sobrien	  if (((unsigned long) value) > 0x00ffffff)
557360484Sobrien	    as_bad_where (fixP->fx_file, fixP->fx_line,
557460484Sobrien			  _("Invalid swi expression"));
557560484Sobrien	  newval = md_chars_to_number (buf, INSN_SIZE) & 0xff000000;
557660484Sobrien	  newval |= value;
557760484Sobrien	  md_number_to_chars (buf, newval , INSN_SIZE);
557860484Sobrien	}
557960484Sobrien      break;
558060484Sobrien
558160484Sobrien    case BFD_RELOC_ARM_MULTI:
558260484Sobrien      if (((unsigned long) value) > 0xffff)
558360484Sobrien	as_bad_where (fixP->fx_file, fixP->fx_line,
558460484Sobrien		      _("Invalid expression in load/store multiple"));
558560484Sobrien      newval = value | md_chars_to_number (buf, INSN_SIZE);
558660484Sobrien      md_number_to_chars (buf, newval, INSN_SIZE);
558760484Sobrien      break;
558860484Sobrien
558960484Sobrien    case BFD_RELOC_ARM_PCREL_BRANCH:
559060484Sobrien      newval = md_chars_to_number (buf, INSN_SIZE);
559160484Sobrien
559260484Sobrien      /* Sign-extend a 24-bit number.  */
559360484Sobrien#define SEXT24(x)	((((x) & 0xffffff) ^ (~ 0x7fffff)) + 0x800000)
559460484Sobrien
559560484Sobrien#ifdef OBJ_ELF
559660484Sobrien      if (! target_oabi)
559760484Sobrien	value = fixP->fx_offset;
559860484Sobrien#endif
559960484Sobrien
560060484Sobrien      /* We are going to store value (shifted right by two) in the
560160484Sobrien	 instruction, in a 24 bit, signed field.  Thus we need to check
560260484Sobrien	 that none of the top 8 bits of the shifted value (top 7 bits of
560360484Sobrien         the unshifted, unsigned value) are set, or that they are all set.  */
560460484Sobrien      if ((value & 0xfe000000UL) != 0
560560484Sobrien	  && ((value & 0xfe000000UL) != 0xfe000000UL))
560660484Sobrien	{
560760484Sobrien#ifdef OBJ_ELF
560860484Sobrien	  /* Normally we would be stuck at this point, since we cannot store
560960484Sobrien	     the absolute address that is the destination of the branch in the
561060484Sobrien	     24 bits of the branch instruction.  If however, we happen to know
561160484Sobrien	     that the destination of the branch is in the same section as the
561260484Sobrien	     branch instruciton itself, then we can compute the relocation for
561360484Sobrien	     ourselves and not have to bother the linker with it.
561460484Sobrien
561560484Sobrien	     FIXME: The tests for OBJ_ELF and ! target_oabi are only here
561660484Sobrien	     because I have not worked out how to do this for OBJ_COFF or
561760484Sobrien	     target_oabi.  */
561860484Sobrien	  if (! target_oabi
561960484Sobrien	      && fixP->fx_addsy != NULL
562060484Sobrien	      && S_IS_DEFINED (fixP->fx_addsy)
562160484Sobrien	      && S_GET_SEGMENT (fixP->fx_addsy) == seg)
562260484Sobrien	    {
562360484Sobrien	      /* Get pc relative value to go into the branch.  */
562460484Sobrien	      value = * val;
562560484Sobrien
562660484Sobrien	      /* Permit a backward branch provided that enough bits are set.
562760484Sobrien		 Allow a forwards branch, provided that enough bits are clear.  */
562860484Sobrien	      if ((value & 0xfe000000UL) == 0xfe000000UL
562960484Sobrien		  || (value & 0xfe000000UL) == 0)
563060484Sobrien		fixP->fx_done = 1;
563160484Sobrien	    }
563260484Sobrien
563360484Sobrien	  if (! fixP->fx_done)
563460484Sobrien#endif
563560484Sobrien	    as_bad_where (fixP->fx_file, fixP->fx_line,
563660484Sobrien			  _("gas can't handle same-section branch dest >= 0x04000000"));
563760484Sobrien	}
563860484Sobrien
563960484Sobrien      value >>= 2;
564060484Sobrien      value += SEXT24 (newval);
564160484Sobrien
564260484Sobrien      if ((value & 0xff000000UL) != 0
564360484Sobrien	  && ((value & 0xff000000UL) != 0xff000000UL))
564460484Sobrien	as_bad_where (fixP->fx_file, fixP->fx_line,
564560484Sobrien		      _("out of range branch"));
564660484Sobrien
564760484Sobrien      newval = (value & 0x00ffffff) | (newval & 0xff000000);
564860484Sobrien      md_number_to_chars (buf, newval, INSN_SIZE);
564960484Sobrien      break;
565060484Sobrien
565160484Sobrien
565260484Sobrien    case BFD_RELOC_THUMB_PCREL_BRANCH9: /* conditional branch */
565360484Sobrien      newval = md_chars_to_number (buf, THUMB_SIZE);
565460484Sobrien      {
565560484Sobrien        addressT diff = (newval & 0xff) << 1;
565660484Sobrien        if (diff & 0x100)
565760484Sobrien         diff |= ~0xff;
565860484Sobrien
565960484Sobrien        value += diff;
566060484Sobrien        if ((value & ~0xff) && ((value & ~0xff) != ~0xff))
566160484Sobrien         as_bad_where (fixP->fx_file, fixP->fx_line,
566260484Sobrien                       _("Branch out of range"));
566360484Sobrien        newval = (newval & 0xff00) | ((value & 0x1ff) >> 1);
566460484Sobrien      }
566560484Sobrien      md_number_to_chars (buf, newval, THUMB_SIZE);
566660484Sobrien      break;
566760484Sobrien
566860484Sobrien    case BFD_RELOC_THUMB_PCREL_BRANCH12: /* unconditional branch */
566960484Sobrien      newval = md_chars_to_number (buf, THUMB_SIZE);
567060484Sobrien      {
567160484Sobrien        addressT diff = (newval & 0x7ff) << 1;
567260484Sobrien        if (diff & 0x800)
567360484Sobrien         diff |= ~0x7ff;
567460484Sobrien
567560484Sobrien        value += diff;
567660484Sobrien        if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff))
567760484Sobrien         as_bad_where (fixP->fx_file, fixP->fx_line,
567860484Sobrien                       _("Branch out of range"));
567960484Sobrien        newval = (newval & 0xf800) | ((value & 0xfff) >> 1);
568060484Sobrien      }
568160484Sobrien      md_number_to_chars (buf, newval, THUMB_SIZE);
568260484Sobrien      break;
568360484Sobrien
568460484Sobrien    case BFD_RELOC_THUMB_PCREL_BRANCH23:
568560484Sobrien      {
568660484Sobrien        offsetT newval2;
568760484Sobrien        addressT diff;
568860484Sobrien
568960484Sobrien	newval  = md_chars_to_number (buf, THUMB_SIZE);
569060484Sobrien        newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
569160484Sobrien        diff = ((newval & 0x7ff) << 12) | ((newval2 & 0x7ff) << 1);
569260484Sobrien        if (diff & 0x400000)
569360484Sobrien	  diff |= ~0x3fffff;
569460484Sobrien#ifdef OBJ_ELF
569560484Sobrien	value = fixP->fx_offset;
569660484Sobrien#endif
569760484Sobrien        value += diff;
569860484Sobrien        if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
569960484Sobrien	  as_bad_where (fixP->fx_file, fixP->fx_line,
570060484Sobrien			_("Branch with link out of range"));
570160484Sobrien
570260484Sobrien        newval  = (newval  & 0xf800) | ((value & 0x7fffff) >> 12);
570360484Sobrien        newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1);
570460484Sobrien        md_number_to_chars (buf, newval, THUMB_SIZE);
570560484Sobrien        md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
570660484Sobrien      }
570760484Sobrien      break;
570860484Sobrien
570960484Sobrien    case BFD_RELOC_8:
571060484Sobrien      if (fixP->fx_done || fixP->fx_pcrel)
571160484Sobrien	md_number_to_chars (buf, value, 1);
571260484Sobrien#ifdef OBJ_ELF
571360484Sobrien      else if (!target_oabi)
571460484Sobrien        {
571560484Sobrien          value = fixP->fx_offset;
571660484Sobrien          md_number_to_chars (buf, value, 1);
571760484Sobrien        }
571860484Sobrien#endif
571960484Sobrien      break;
572060484Sobrien
572160484Sobrien    case BFD_RELOC_16:
572260484Sobrien      if (fixP->fx_done || fixP->fx_pcrel)
572360484Sobrien	md_number_to_chars (buf, value, 2);
572460484Sobrien#ifdef OBJ_ELF
572560484Sobrien      else if (!target_oabi)
572660484Sobrien        {
572760484Sobrien          value = fixP->fx_offset;
572860484Sobrien          md_number_to_chars (buf, value, 2);
572960484Sobrien        }
573060484Sobrien#endif
573160484Sobrien      break;
573260484Sobrien
573360484Sobrien#ifdef OBJ_ELF
573460484Sobrien    case BFD_RELOC_ARM_GOT32:
573560484Sobrien    case BFD_RELOC_ARM_GOTOFF:
573660484Sobrien	md_number_to_chars (buf, 0, 4);
573760484Sobrien	break;
573860484Sobrien#endif
573960484Sobrien
574060484Sobrien    case BFD_RELOC_RVA:
574160484Sobrien    case BFD_RELOC_32:
574260484Sobrien      if (fixP->fx_done || fixP->fx_pcrel)
574360484Sobrien	md_number_to_chars (buf, value, 4);
574460484Sobrien#ifdef OBJ_ELF
574560484Sobrien      else if (!target_oabi)
574660484Sobrien        {
574760484Sobrien          value = fixP->fx_offset;
574860484Sobrien          md_number_to_chars (buf, value, 4);
574960484Sobrien        }
575060484Sobrien#endif
575160484Sobrien      break;
575260484Sobrien
575360484Sobrien#ifdef OBJ_ELF
575460484Sobrien    case BFD_RELOC_ARM_PLT32:
575560484Sobrien      /* It appears the instruction is fully prepared at this point. */
575660484Sobrien      break;
575760484Sobrien#endif
575860484Sobrien
575960484Sobrien    case BFD_RELOC_ARM_GOTPC:
576060484Sobrien      md_number_to_chars (buf, value, 4);
576160484Sobrien      break;
576260484Sobrien
576360484Sobrien    case BFD_RELOC_ARM_CP_OFF_IMM:
576460484Sobrien      sign = value >= 0;
576560484Sobrien      if (value < -1023 || value > 1023 || (value & 3))
576660484Sobrien	as_bad_where (fixP->fx_file, fixP->fx_line,
576760484Sobrien		      _("Illegal value for co-processor offset"));
576860484Sobrien      if (value < 0)
576960484Sobrien	value = -value;
577060484Sobrien      newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00;
577160484Sobrien      newval |= (value >> 2) | (sign ?  INDEX_UP : 0);
577260484Sobrien      md_number_to_chars (buf, newval , INSN_SIZE);
577360484Sobrien      break;
577460484Sobrien
577560484Sobrien    case BFD_RELOC_ARM_THUMB_OFFSET:
577660484Sobrien      newval = md_chars_to_number (buf, THUMB_SIZE);
577760484Sobrien      /* Exactly what ranges, and where the offset is inserted depends on
577860484Sobrien	 the type of instruction, we can establish this from the top 4 bits */
577960484Sobrien      switch (newval >> 12)
578060484Sobrien	{
578160484Sobrien	case 4: /* PC load */
578260484Sobrien	  /* Thumb PC loads are somewhat odd, bit 1 of the PC is
578360484Sobrien	     forced to zero for these loads, so we will need to round
578460484Sobrien	     up the offset if the instruction address is not word
578560484Sobrien	     aligned (since the final address produced must be, and
578660484Sobrien	     we can only describe word-aligned immediate offsets).  */
578760484Sobrien
578860484Sobrien	  if ((fixP->fx_frag->fr_address + fixP->fx_where + value) & 3)
578960484Sobrien	    as_bad_where (fixP->fx_file, fixP->fx_line,
579060484Sobrien			  _("Invalid offset, target not word aligned (0x%08X)"),
579160484Sobrien                          (unsigned int)(fixP->fx_frag->fr_address + fixP->fx_where + value));
579260484Sobrien
579360484Sobrien	  if ((value + 2) & ~0x3fe)
579460484Sobrien	    as_bad_where (fixP->fx_file, fixP->fx_line,
579560484Sobrien			  _("Invalid offset, value too big (0x%08X)"), value);
579660484Sobrien
579760484Sobrien          /* Round up, since pc will be rounded down.  */
579860484Sobrien	  newval |= (value + 2) >> 2;
579960484Sobrien	  break;
580060484Sobrien
580160484Sobrien	case 9: /* SP load/store */
580260484Sobrien	  if (value & ~0x3fc)
580360484Sobrien	    as_bad_where (fixP->fx_file, fixP->fx_line,
580460484Sobrien			  _("Invalid offset, value too big (0x%08X)"), value);
580560484Sobrien	  newval |= value >> 2;
580660484Sobrien	  break;
580760484Sobrien
580860484Sobrien	case 6: /* Word load/store */
580960484Sobrien	  if (value & ~0x7c)
581060484Sobrien	    as_bad_where (fixP->fx_file, fixP->fx_line,
581160484Sobrien			  _("Invalid offset, value too big (0x%08X)"), value);
581260484Sobrien	  newval |= value << 4; /* 6 - 2 */
581360484Sobrien	  break;
581460484Sobrien
581560484Sobrien	case 7: /* Byte load/store */
581660484Sobrien	  if (value & ~0x1f)
581760484Sobrien	    as_bad_where (fixP->fx_file, fixP->fx_line,
581860484Sobrien			  _("Invalid offset, value too big (0x%08X)"), value);
581960484Sobrien	  newval |= value << 6;
582060484Sobrien	  break;
582160484Sobrien
582260484Sobrien	case 8: /* Halfword load/store */
582360484Sobrien	  if (value & ~0x3e)
582460484Sobrien	    as_bad_where (fixP->fx_file, fixP->fx_line,
582560484Sobrien			  _("Invalid offset, value too big (0x%08X)"), value);
582660484Sobrien	  newval |= value << 5; /* 6 - 1 */
582760484Sobrien	  break;
582860484Sobrien
582960484Sobrien	default:
583060484Sobrien	  as_bad_where (fixP->fx_file, fixP->fx_line,
583160484Sobrien			"Unable to process relocation for thumb opcode: %lx",
583260484Sobrien			(unsigned long) newval);
583360484Sobrien	  break;
583460484Sobrien	}
583560484Sobrien      md_number_to_chars (buf, newval, THUMB_SIZE);
583660484Sobrien      break;
583760484Sobrien
583860484Sobrien    case BFD_RELOC_ARM_THUMB_ADD:
583960484Sobrien      /* This is a complicated relocation, since we use it for all of
584060484Sobrien         the following immediate relocations:
584160484Sobrien            3bit ADD/SUB
584260484Sobrien            8bit ADD/SUB
584360484Sobrien            9bit ADD/SUB SP word-aligned
584460484Sobrien           10bit ADD PC/SP word-aligned
584560484Sobrien
584660484Sobrien         The type of instruction being processed is encoded in the
584760484Sobrien         instruction field:
584860484Sobrien           0x8000  SUB
584960484Sobrien           0x00F0  Rd
585060484Sobrien           0x000F  Rs
585160484Sobrien      */
585260484Sobrien      newval = md_chars_to_number (buf, THUMB_SIZE);
585360484Sobrien      {
585460484Sobrien        int rd = (newval >> 4) & 0xf;
585560484Sobrien        int rs = newval & 0xf;
585660484Sobrien        int subtract = newval & 0x8000;
585760484Sobrien
585860484Sobrien        if (rd == REG_SP)
585960484Sobrien          {
586060484Sobrien            if (value & ~0x1fc)
586160484Sobrien              as_bad_where (fixP->fx_file, fixP->fx_line,
586260484Sobrien                            _("Invalid immediate for stack address calculation"));
586360484Sobrien            newval = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
586460484Sobrien            newval |= value >> 2;
586560484Sobrien          }
586660484Sobrien        else if (rs == REG_PC || rs == REG_SP)
586760484Sobrien          {
586860484Sobrien            if (subtract ||
586960484Sobrien                value & ~0x3fc)
587060484Sobrien              as_bad_where (fixP->fx_file, fixP->fx_line,
587160484Sobrien                            _("Invalid immediate for address calculation (value = 0x%08lX)"),
587260484Sobrien			    (unsigned long) value);
587360484Sobrien            newval = (rs == REG_PC ? T_OPCODE_ADD_PC : T_OPCODE_ADD_SP);
587460484Sobrien            newval |= rd << 8;
587560484Sobrien            newval |= value >> 2;
587660484Sobrien          }
587760484Sobrien        else if (rs == rd)
587860484Sobrien          {
587960484Sobrien            if (value & ~0xff)
588060484Sobrien              as_bad_where (fixP->fx_file, fixP->fx_line,
588160484Sobrien                            _("Invalid 8bit immediate"));
588260484Sobrien            newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
588360484Sobrien            newval |= (rd << 8) | value;
588460484Sobrien          }
588560484Sobrien        else
588660484Sobrien          {
588760484Sobrien            if (value & ~0x7)
588860484Sobrien              as_bad_where (fixP->fx_file, fixP->fx_line,
588960484Sobrien                            _("Invalid 3bit immediate"));
589060484Sobrien            newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
589160484Sobrien            newval |= rd | (rs << 3) | (value << 6);
589260484Sobrien          }
589360484Sobrien      }
589460484Sobrien      md_number_to_chars (buf, newval , THUMB_SIZE);
589560484Sobrien      break;
589660484Sobrien
589760484Sobrien    case BFD_RELOC_ARM_THUMB_IMM:
589860484Sobrien      newval = md_chars_to_number (buf, THUMB_SIZE);
589960484Sobrien      switch (newval >> 11)
590060484Sobrien        {
590160484Sobrien        case 0x04: /* 8bit immediate MOV */
590260484Sobrien        case 0x05: /* 8bit immediate CMP */
590360484Sobrien          if (value < 0 || value > 255)
590460484Sobrien            as_bad_where (fixP->fx_file, fixP->fx_line,
590560484Sobrien                          _("Invalid immediate: %ld is too large"),
590660484Sobrien			  (long) value);
590760484Sobrien          newval |= value;
590860484Sobrien          break;
590960484Sobrien
591060484Sobrien        default:
591160484Sobrien          abort ();
591260484Sobrien        }
591360484Sobrien      md_number_to_chars (buf, newval , THUMB_SIZE);
591460484Sobrien      break;
591560484Sobrien
591660484Sobrien    case BFD_RELOC_ARM_THUMB_SHIFT:
591760484Sobrien      /* 5bit shift value (0..31) */
591860484Sobrien      if (value < 0 || value > 31)
591960484Sobrien	as_bad_where (fixP->fx_file, fixP->fx_line,
592060484Sobrien		      _("Illegal Thumb shift value: %ld"), (long) value);
592160484Sobrien      newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf03f;
592260484Sobrien      newval |= value << 6;
592360484Sobrien      md_number_to_chars (buf, newval , THUMB_SIZE);
592460484Sobrien      break;
592560484Sobrien
592660484Sobrien    case BFD_RELOC_VTABLE_INHERIT:
592760484Sobrien    case BFD_RELOC_VTABLE_ENTRY:
592860484Sobrien      fixP->fx_done = 0;
592960484Sobrien      return 1;
593060484Sobrien
593160484Sobrien    case BFD_RELOC_NONE:
593260484Sobrien    default:
593360484Sobrien      as_bad_where (fixP->fx_file, fixP->fx_line,
593460484Sobrien		    _("Bad relocation fixup type (%d)"), fixP->fx_r_type);
593560484Sobrien    }
593660484Sobrien
593760484Sobrien  return 1;
593860484Sobrien}
593960484Sobrien
594060484Sobrien/* Translate internal representation of relocation info to BFD target
594160484Sobrien   format.  */
594260484Sobrienarelent *
594360484Sobrientc_gen_reloc (section, fixp)
594460484Sobrien     asection * section ATTRIBUTE_UNUSED;
594560484Sobrien     fixS * fixp;
594660484Sobrien{
594760484Sobrien  arelent * reloc;
594860484Sobrien  bfd_reloc_code_real_type code;
594960484Sobrien
595060484Sobrien  reloc = (arelent *) xmalloc (sizeof (arelent));
595160484Sobrien
595260484Sobrien  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
595360484Sobrien  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
595460484Sobrien  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
595560484Sobrien
595660484Sobrien  /* @@ Why fx_addnumber sometimes and fx_offset other times?  */
595760484Sobrien#ifndef OBJ_ELF
595860484Sobrien  if (fixp->fx_pcrel == 0)
595960484Sobrien    reloc->addend = fixp->fx_offset;
596060484Sobrien  else
596160484Sobrien    reloc->addend = fixp->fx_offset = reloc->address;
596260484Sobrien#else  /* OBJ_ELF */
596360484Sobrien  reloc->addend = fixp->fx_offset;
596460484Sobrien#endif
596560484Sobrien
596660484Sobrien  switch (fixp->fx_r_type)
596760484Sobrien    {
596860484Sobrien    case BFD_RELOC_8:
596960484Sobrien      if (fixp->fx_pcrel)
597060484Sobrien	{
597160484Sobrien	  code = BFD_RELOC_8_PCREL;
597260484Sobrien	  break;
597360484Sobrien	}
597460484Sobrien
597560484Sobrien    case BFD_RELOC_16:
597660484Sobrien      if (fixp->fx_pcrel)
597760484Sobrien	{
597860484Sobrien	  code = BFD_RELOC_16_PCREL;
597960484Sobrien	  break;
598060484Sobrien	}
598160484Sobrien
598260484Sobrien    case BFD_RELOC_32:
598360484Sobrien      if (fixp->fx_pcrel)
598460484Sobrien	{
598560484Sobrien	  code = BFD_RELOC_32_PCREL;
598660484Sobrien	  break;
598760484Sobrien	}
598860484Sobrien
598960484Sobrien    case BFD_RELOC_ARM_PCREL_BRANCH:
599060484Sobrien    case BFD_RELOC_RVA:
599160484Sobrien    case BFD_RELOC_THUMB_PCREL_BRANCH9:
599260484Sobrien    case BFD_RELOC_THUMB_PCREL_BRANCH12:
599360484Sobrien    case BFD_RELOC_THUMB_PCREL_BRANCH23:
599460484Sobrien    case BFD_RELOC_VTABLE_ENTRY:
599560484Sobrien    case BFD_RELOC_VTABLE_INHERIT:
599660484Sobrien      code = fixp->fx_r_type;
599760484Sobrien      break;
599860484Sobrien
599960484Sobrien    case BFD_RELOC_ARM_LITERAL:
600060484Sobrien    case BFD_RELOC_ARM_HWLITERAL:
600160484Sobrien      /* If this is called then the a literal has been referenced across
600260484Sobrien	 a section boundary - possibly due to an implicit dump */
600360484Sobrien      as_bad_where (fixp->fx_file, fixp->fx_line,
600460484Sobrien		    _("Literal referenced across section boundary (Implicit dump?)"));
600560484Sobrien      return NULL;
600660484Sobrien
600760484Sobrien#ifdef OBJ_ELF
600860484Sobrien    case BFD_RELOC_ARM_GOT32:
600960484Sobrien    case BFD_RELOC_ARM_GOTOFF:
601060484Sobrien    case BFD_RELOC_ARM_PLT32:
601160484Sobrien       code = fixp->fx_r_type;
601260484Sobrien    break;
601360484Sobrien#endif
601460484Sobrien
601560484Sobrien    case BFD_RELOC_ARM_IMMEDIATE:
601660484Sobrien      as_bad_where (fixp->fx_file, fixp->fx_line,
601760484Sobrien		    _("Internal_relocation (type %d) not fixed up (IMMEDIATE)"),
601860484Sobrien		    fixp->fx_r_type);
601960484Sobrien      return NULL;
602060484Sobrien
602160484Sobrien    case BFD_RELOC_ARM_ADRL_IMMEDIATE:
602260484Sobrien      as_bad_where (fixp->fx_file, fixp->fx_line,
602360484Sobrien		    _("ADRL used for a symbol not defined in the same file"),
602460484Sobrien		    fixp->fx_r_type);
602560484Sobrien      return NULL;
602660484Sobrien
602760484Sobrien    case BFD_RELOC_ARM_OFFSET_IMM:
602860484Sobrien      as_bad_where (fixp->fx_file, fixp->fx_line,
602960484Sobrien		    _("Internal_relocation (type %d) not fixed up (OFFSET_IMM)"),
603060484Sobrien		    fixp->fx_r_type);
603160484Sobrien      return NULL;
603260484Sobrien
603360484Sobrien    default:
603460484Sobrien      {
603560484Sobrien	char * type;
603660484Sobrien	switch (fixp->fx_r_type)
603760484Sobrien	  {
603860484Sobrien	  case BFD_RELOC_ARM_IMMEDIATE:    type = "IMMEDIATE";    break;
603960484Sobrien	  case BFD_RELOC_ARM_OFFSET_IMM:   type = "OFFSET_IMM";   break;
604060484Sobrien	  case BFD_RELOC_ARM_OFFSET_IMM8:  type = "OFFSET_IMM8";  break;
604160484Sobrien	  case BFD_RELOC_ARM_SHIFT_IMM:    type = "SHIFT_IMM";    break;
604260484Sobrien	  case BFD_RELOC_ARM_SWI:          type = "SWI";          break;
604360484Sobrien	  case BFD_RELOC_ARM_MULTI:        type = "MULTI";        break;
604460484Sobrien	  case BFD_RELOC_ARM_CP_OFF_IMM:   type = "CP_OFF_IMM";   break;
604560484Sobrien	  case BFD_RELOC_ARM_THUMB_ADD:    type = "THUMB_ADD";    break;
604660484Sobrien	  case BFD_RELOC_ARM_THUMB_SHIFT:  type = "THUMB_SHIFT";  break;
604760484Sobrien	  case BFD_RELOC_ARM_THUMB_IMM:    type = "THUMB_IMM";    break;
604860484Sobrien	  case BFD_RELOC_ARM_THUMB_OFFSET: type = "THUMB_OFFSET"; break;
604960484Sobrien	  default:                         type = _("<unknown>"); break;
605060484Sobrien	  }
605160484Sobrien	as_bad_where (fixp->fx_file, fixp->fx_line,
605260484Sobrien		      _("Can not represent %s relocation in this object file format (%d)"),
605360484Sobrien		      type, fixp->fx_pcrel);
605460484Sobrien	return NULL;
605560484Sobrien      }
605660484Sobrien    }
605760484Sobrien
605860484Sobrien#ifdef OBJ_ELF
605960484Sobrien if (code == BFD_RELOC_32_PCREL
606060484Sobrien     && GOT_symbol
606160484Sobrien     && fixp->fx_addsy == GOT_symbol)
606260484Sobrien   {
606360484Sobrien     code = BFD_RELOC_ARM_GOTPC;
606460484Sobrien     reloc->addend = fixp->fx_offset = reloc->address;
606560484Sobrien   }
606660484Sobrien#endif
606760484Sobrien
606860484Sobrien  reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
606960484Sobrien
607060484Sobrien  if (reloc->howto == NULL)
607160484Sobrien    {
607260484Sobrien      as_bad_where (fixp->fx_file, fixp->fx_line,
607360484Sobrien		    _("Can not represent %s relocation in this object file format"),
607460484Sobrien		    bfd_get_reloc_code_name (code));
607560484Sobrien      return NULL;
607660484Sobrien    }
607760484Sobrien
607860484Sobrien   /* HACK: Since arm ELF uses Rel instead of Rela, encode the
607960484Sobrien      vtable entry to be used in the relocation's section offset.  */
608060484Sobrien   if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
608160484Sobrien     reloc->address = fixp->fx_offset;
608260484Sobrien
608360484Sobrien  return reloc;
608460484Sobrien}
608560484Sobrien
608660484Sobrienint
608760484Sobrienmd_estimate_size_before_relax (fragP, segtype)
608860484Sobrien     fragS * fragP ATTRIBUTE_UNUSED;
608960484Sobrien     segT    segtype ATTRIBUTE_UNUSED;
609060484Sobrien{
609160484Sobrien  as_fatal (_("md_estimate_size_before_relax\n"));
609260484Sobrien  return 1;
609360484Sobrien}
609460484Sobrien
609560484Sobrienstatic void
609660484Sobrienoutput_inst PARAMS ((void))
609760484Sobrien{
609860484Sobrien  char * to = NULL;
609960484Sobrien
610060484Sobrien  if (inst.error)
610160484Sobrien    {
610260484Sobrien      as_bad (inst.error);
610360484Sobrien      return;
610460484Sobrien    }
610560484Sobrien
610660484Sobrien  to = frag_more (inst.size);
610760484Sobrien
610860484Sobrien  if (thumb_mode && (inst.size > THUMB_SIZE))
610960484Sobrien    {
611060484Sobrien      assert (inst.size == (2 * THUMB_SIZE));
611160484Sobrien      md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE);
611260484Sobrien      md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE);
611360484Sobrien    }
611460484Sobrien  else if (inst.size > INSN_SIZE)
611560484Sobrien    {
611660484Sobrien      assert (inst.size == (2 * INSN_SIZE));
611760484Sobrien      md_number_to_chars (to, inst.instruction, INSN_SIZE);
611860484Sobrien      md_number_to_chars (to + INSN_SIZE, inst.instruction, INSN_SIZE);
611960484Sobrien    }
612060484Sobrien  else
612160484Sobrien    md_number_to_chars (to, inst.instruction, inst.size);
612260484Sobrien
612360484Sobrien  if (inst.reloc.type != BFD_RELOC_NONE)
612460484Sobrien    fix_new_arm (frag_now, to - frag_now->fr_literal,
612560484Sobrien		 inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
612660484Sobrien		 inst.reloc.type);
612760484Sobrien
612860484Sobrien  return;
612960484Sobrien}
613060484Sobrien
613160484Sobrienvoid
613260484Sobrienmd_assemble (str)
613360484Sobrien     char * str;
613460484Sobrien{
613560484Sobrien  char   c;
613660484Sobrien  char * p;
613760484Sobrien  char * q;
613860484Sobrien  char * start;
613960484Sobrien
614060484Sobrien  /* Align the instruction.
614160484Sobrien     This may not be the right thing to do but ... */
614260484Sobrien  /* arm_align (2, 0); */
614360484Sobrien  listing_prev_line (); /* Defined in listing.h */
614460484Sobrien
614560484Sobrien  /* Align the previous label if needed.  */
614660484Sobrien  if (last_label_seen != NULL)
614760484Sobrien    {
614860484Sobrien      symbol_set_frag (last_label_seen, frag_now);
614960484Sobrien      S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ());
615060484Sobrien      S_SET_SEGMENT (last_label_seen, now_seg);
615160484Sobrien    }
615260484Sobrien
615360484Sobrien  memset (&inst, '\0', sizeof (inst));
615460484Sobrien  inst.reloc.type = BFD_RELOC_NONE;
615560484Sobrien
615660484Sobrien  skip_whitespace (str);
615760484Sobrien
615860484Sobrien  /* Scan up to the end of the op-code, which must end in white space or
615960484Sobrien     end of string.  */
616060484Sobrien  for (start = p = str; *p != '\0'; p++)
616160484Sobrien    if (*p == ' ')
616260484Sobrien      break;
616360484Sobrien
616460484Sobrien  if (p == str)
616560484Sobrien    {
616660484Sobrien      as_bad (_("No operator -- statement `%s'\n"), str);
616760484Sobrien      return;
616860484Sobrien    }
616960484Sobrien
617060484Sobrien  if (thumb_mode)
617160484Sobrien    {
617260484Sobrien      CONST struct thumb_opcode * opcode;
617360484Sobrien
617460484Sobrien      c = *p;
617560484Sobrien      *p = '\0';
617660484Sobrien      opcode = (CONST struct thumb_opcode *) hash_find (arm_tops_hsh, str);
617760484Sobrien      *p = c;
617860484Sobrien
617960484Sobrien      if (opcode)
618060484Sobrien	{
618160484Sobrien	  /* Check that this instruction is supported for this CPU.  */
618260484Sobrien	  if (thumb_mode == 1 && (opcode->variants & cpu_variant) == 0)
618360484Sobrien	     {
618460484Sobrien	    	as_bad (_("selected processor does not support this opcode"));
618560484Sobrien		return;
618660484Sobrien	     }
618760484Sobrien
618860484Sobrien	  inst.instruction = opcode->value;
618960484Sobrien	  inst.size = opcode->size;
619060484Sobrien	  (*opcode->parms)(p);
619160484Sobrien	  output_inst ();
619260484Sobrien	  return;
619360484Sobrien	}
619460484Sobrien    }
619560484Sobrien  else
619660484Sobrien    {
619760484Sobrien      CONST struct asm_opcode * opcode;
619860484Sobrien      unsigned long cond_code;
619960484Sobrien
620060484Sobrien      inst.size = INSN_SIZE;
620160484Sobrien      /* p now points to the end of the opcode, probably white space, but we
620260484Sobrien	 have to break the opcode up in case it contains condionals and flags;
620360484Sobrien	 keep trying with progressively smaller basic instructions until one
620460484Sobrien	 matches, or we run out of opcode.  */
620560484Sobrien      q = (p - str > LONGEST_INST) ? str + LONGEST_INST : p;
620660484Sobrien      for (; q != str; q--)
620760484Sobrien	{
620860484Sobrien	  c = *q;
620960484Sobrien	  *q = '\0';
621060484Sobrien	  opcode = (CONST struct asm_opcode *) hash_find (arm_ops_hsh, str);
621160484Sobrien	  *q = c;
621260484Sobrien
621360484Sobrien	  if (opcode && opcode->template)
621460484Sobrien	    {
621560484Sobrien	      unsigned long flag_bits = 0;
621660484Sobrien	      char * r;
621760484Sobrien
621860484Sobrien	      /* Check that this instruction is supported for this CPU.  */
621960484Sobrien	      if ((opcode->variants & cpu_variant) == 0)
622060484Sobrien		goto try_shorter;
622160484Sobrien
622260484Sobrien	      inst.instruction = opcode->value;
622360484Sobrien	      if (q == p)		/* Just a simple opcode.  */
622460484Sobrien		{
622560484Sobrien		  if (opcode->comp_suffix)
622660484Sobrien		    {
622760484Sobrien		       if (*opcode->comp_suffix != '\0')
622860484Sobrien		    	 as_bad (_("Opcode `%s' must have suffix from list: <%s>"),
622960484Sobrien			     str, opcode->comp_suffix);
623060484Sobrien		       else
623160484Sobrien			 /* Not a conditional instruction. */
623260484Sobrien		         (*opcode->parms)(q, 0);
623360484Sobrien		    }
623460484Sobrien		  else
623560484Sobrien		    {
623660484Sobrien		      /* A conditional instruction with default condition. */
623760484Sobrien		      inst.instruction |= COND_ALWAYS;
623860484Sobrien		      (*opcode->parms)(q, 0);
623960484Sobrien		    }
624060484Sobrien		  output_inst ();
624160484Sobrien		  return;
624260484Sobrien		}
624360484Sobrien
624460484Sobrien	      /* Not just a simple opcode.  Check if extra is a conditional. */
624560484Sobrien	      r = q;
624660484Sobrien	      if (p - r >= 2)
624760484Sobrien		{
624860484Sobrien		  CONST struct asm_cond *cond;
624960484Sobrien		  char d = *(r + 2);
625060484Sobrien
625160484Sobrien		  *(r + 2) = '\0';
625260484Sobrien		  cond = (CONST struct asm_cond *) hash_find (arm_cond_hsh, r);
625360484Sobrien		  *(r + 2) = d;
625460484Sobrien		  if (cond)
625560484Sobrien		    {
625660484Sobrien		      if (cond->value == 0xf0000000)
625760484Sobrien			as_tsktsk (
625860484Sobrien_("Warning: Use of the 'nv' conditional is deprecated\n"));
625960484Sobrien
626060484Sobrien		      cond_code = cond->value;
626160484Sobrien		      r += 2;
626260484Sobrien		    }
626360484Sobrien		  else
626460484Sobrien		    cond_code = COND_ALWAYS;
626560484Sobrien		}
626660484Sobrien	      else
626760484Sobrien		cond_code = COND_ALWAYS;
626860484Sobrien
626960484Sobrien	      /* Apply the conditional, or complain it's not allowed. */
627060484Sobrien	      if (opcode->comp_suffix && *opcode->comp_suffix == '\0')
627160484Sobrien		{
627260484Sobrien		   /* Instruction isn't conditional */
627360484Sobrien		   if (cond_code != COND_ALWAYS)
627460484Sobrien		     {
627560484Sobrien		       as_bad (_("Opcode `%s' is unconditional\n"), str);
627660484Sobrien		       return;
627760484Sobrien		     }
627860484Sobrien		}
627960484Sobrien	      else
628060484Sobrien		/* Instruction is conditional: set the condition into it. */
628160484Sobrien		inst.instruction |= cond_code;
628260484Sobrien
628360484Sobrien
628460484Sobrien	      /* If there is a compulsory suffix, it should come here, before
628560484Sobrien		 any optional flags.  */
628660484Sobrien	      if (opcode->comp_suffix && *opcode->comp_suffix != '\0')
628760484Sobrien		{
628860484Sobrien		  CONST char *s = opcode->comp_suffix;
628960484Sobrien
629060484Sobrien		  while (*s)
629160484Sobrien		    {
629260484Sobrien		      inst.suffix++;
629360484Sobrien		      if (*r == *s)
629460484Sobrien			break;
629560484Sobrien		      s++;
629660484Sobrien		    }
629760484Sobrien
629860484Sobrien		  if (*s == '\0')
629960484Sobrien		    {
630060484Sobrien		      as_bad (_("Opcode `%s' must have suffix from <%s>\n"), str,
630160484Sobrien			      opcode->comp_suffix);
630260484Sobrien		      return;
630360484Sobrien		    }
630460484Sobrien
630560484Sobrien		  r++;
630660484Sobrien		}
630760484Sobrien
630860484Sobrien	      /* The remainder, if any should now be flags for the instruction;
630960484Sobrien		 Scan these checking each one found with the opcode.  */
631060484Sobrien	      if (r != p)
631160484Sobrien		{
631260484Sobrien		  char d;
631360484Sobrien		  CONST struct asm_flg *flag = opcode->flags;
631460484Sobrien
631560484Sobrien		  if (flag)
631660484Sobrien		    {
631760484Sobrien		      int flagno;
631860484Sobrien
631960484Sobrien		      d = *p;
632060484Sobrien		      *p = '\0';
632160484Sobrien
632260484Sobrien		      for (flagno = 0; flag[flagno].template; flagno++)
632360484Sobrien			{
632460484Sobrien			  if (streq (r, flag[flagno].template))
632560484Sobrien			    {
632660484Sobrien			      flag_bits |= flag[flagno].set_bits;
632760484Sobrien			      break;
632860484Sobrien			    }
632960484Sobrien			}
633060484Sobrien
633160484Sobrien		      *p = d;
633260484Sobrien		      if (! flag[flagno].template)
633360484Sobrien			goto try_shorter;
633460484Sobrien		    }
633560484Sobrien		  else
633660484Sobrien		    goto try_shorter;
633760484Sobrien		}
633860484Sobrien
633960484Sobrien	      (*opcode->parms) (p, flag_bits);
634060484Sobrien	      output_inst ();
634160484Sobrien	      return;
634260484Sobrien	    }
634360484Sobrien
634460484Sobrien	try_shorter:
634560484Sobrien	  ;
634660484Sobrien	}
634760484Sobrien    }
634860484Sobrien
634960484Sobrien  /* It wasn't an instruction, but it might be a register alias of the form
635060484Sobrien     alias .req reg */
635160484Sobrien  q = p;
635260484Sobrien  skip_whitespace (q);
635360484Sobrien
635460484Sobrien  c = *p;
635560484Sobrien  *p = '\0';
635660484Sobrien
635760484Sobrien  if (*q && !strncmp (q, ".req ", 4))
635860484Sobrien    {
635960484Sobrien      int    reg;
636060484Sobrien      char * copy_of_str = str;
636160484Sobrien      char * r;
636260484Sobrien
636360484Sobrien      q += 4;
636460484Sobrien      skip_whitespace (q);
636560484Sobrien
636660484Sobrien      for (r = q; *r != '\0'; r++)
636760484Sobrien	if (*r == ' ')
636860484Sobrien	  break;
636960484Sobrien
637060484Sobrien      if (r != q)
637160484Sobrien	{
637260484Sobrien	  int regnum;
637360484Sobrien	  char d = *r;
637460484Sobrien
637560484Sobrien	  *r = '\0';
637660484Sobrien	  regnum = arm_reg_parse (& q);
637760484Sobrien	  *r = d;
637860484Sobrien
637960484Sobrien	  reg = arm_reg_parse (& str);
638060484Sobrien
638160484Sobrien	  if (reg == FAIL)
638260484Sobrien	    {
638360484Sobrien	      if (regnum != FAIL)
638460484Sobrien		insert_reg_alias (str, regnum);
638560484Sobrien	      else
638660484Sobrien		as_warn (_("register '%s' does not exist\n"), q);
638760484Sobrien	    }
638860484Sobrien	  else if (regnum != FAIL)
638960484Sobrien	    {
639060484Sobrien	      if (reg != regnum)
639160484Sobrien		as_warn (_("ignoring redefinition of register alias '%s'"),
639260484Sobrien			 copy_of_str );
639360484Sobrien
639460484Sobrien	      /* Do not warn about redefinitions to the same alias.  */
639560484Sobrien	    }
639660484Sobrien	  else
639760484Sobrien	    as_warn (_("ignoring redefinition of register alias '%s' to non-existant register '%s'"),
639860484Sobrien		     copy_of_str, q);
639960484Sobrien	}
640060484Sobrien      else
640160484Sobrien	as_warn (_("ignoring incomplete .req pseuso op"));
640260484Sobrien
640360484Sobrien      *p = c;
640460484Sobrien      return;
640560484Sobrien    }
640660484Sobrien
640760484Sobrien  *p = c;
640860484Sobrien  as_bad (_("bad instruction `%s'"), start);
640960484Sobrien}
641060484Sobrien
641160484Sobrien/*
641260484Sobrien * md_parse_option
641360484Sobrien *    Invocation line includes a switch not recognized by the base assembler.
641460484Sobrien *    See if it's a processor-specific option.  These are:
641560484Sobrien *    Cpu variants, the arm part is optional:
641660484Sobrien *            -m[arm]1                Currently not supported.
641760484Sobrien *            -m[arm]2, -m[arm]250    Arm 2 and Arm 250 processor
641860484Sobrien *            -m[arm]3                Arm 3 processor
641960484Sobrien *            -m[arm]6[xx],           Arm 6 processors
642060484Sobrien *            -m[arm]7[xx][t][[d]m]   Arm 7 processors
642160484Sobrien *            -m[arm]8[10]            Arm 8 processors
642260484Sobrien *            -m[arm]9[20][tdmi]      Arm 9 processors
642360484Sobrien *            -mstrongarm[110[0]]     StrongARM processors
642461843Sobrien *            -m[arm]v[2345[t]]       Arm architectures
642560484Sobrien *            -mall                   All (except the ARM1)
642660484Sobrien *    FP variants:
642760484Sobrien *            -mfpa10, -mfpa11        FPA10 and 11 co-processor instructions
642860484Sobrien *            -mfpe-old               (No float load/store multiples)
642960484Sobrien *            -mno-fpu                Disable all floating point instructions
643060484Sobrien *    Run-time endian selection:
643160484Sobrien *            -EB                     big endian cpu
643260484Sobrien *            -EL                     little endian cpu
643360484Sobrien *    ARM Procedure Calling Standard:
643460484Sobrien *	      -mapcs-32		      32 bit APCS
643560484Sobrien *	      -mapcs-26		      26 bit APCS
643660484Sobrien *	      -mapcs-float	      Pass floats in float regs
643760484Sobrien *	      -mapcs-reentrant        Position independent code
643860484Sobrien *            -mthumb-interwork       Code supports Arm/Thumb interworking
643960484Sobrien *            -moabi                  Old ELF ABI
644060484Sobrien */
644160484Sobrien
644260484SobrienCONST char * md_shortopts = "m:k";
644360484Sobrienstruct option md_longopts[] =
644460484Sobrien{
644560484Sobrien#ifdef ARM_BI_ENDIAN
644660484Sobrien#define OPTION_EB (OPTION_MD_BASE + 0)
644760484Sobrien  {"EB", no_argument, NULL, OPTION_EB},
644860484Sobrien#define OPTION_EL (OPTION_MD_BASE + 1)
644960484Sobrien  {"EL", no_argument, NULL, OPTION_EL},
645060484Sobrien#ifdef OBJ_ELF
645160484Sobrien#define OPTION_OABI (OPTION_MD_BASE +2)
645260484Sobrien  {"oabi", no_argument, NULL, OPTION_OABI},
645360484Sobrien#endif
645460484Sobrien#endif
645560484Sobrien  {NULL, no_argument, NULL, 0}
645660484Sobrien};
645760484Sobriensize_t md_longopts_size = sizeof (md_longopts);
645860484Sobrien
645960484Sobrienint
646060484Sobrienmd_parse_option (c, arg)
646160484Sobrien     int    c;
646260484Sobrien     char * arg;
646360484Sobrien{
646460484Sobrien  char * str = arg;
646560484Sobrien
646660484Sobrien  switch (c)
646760484Sobrien    {
646860484Sobrien#ifdef ARM_BI_ENDIAN
646960484Sobrien    case OPTION_EB:
647060484Sobrien      target_big_endian = 1;
647160484Sobrien      break;
647260484Sobrien    case OPTION_EL:
647360484Sobrien      target_big_endian = 0;
647460484Sobrien      break;
647560484Sobrien#endif
647660484Sobrien
647760484Sobrien    case 'm':
647860484Sobrien      switch (*str)
647960484Sobrien	{
648060484Sobrien	case 'f':
648160484Sobrien	  if (streq (str, "fpa10"))
648260484Sobrien	    cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_FPA10;
648360484Sobrien	  else if (streq (str, "fpa11"))
648460484Sobrien	    cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_FPA11;
648560484Sobrien	  else if (streq (str, "fpe-old"))
648660484Sobrien	    cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_CORE;
648760484Sobrien	  else
648860484Sobrien	    goto bad;
648960484Sobrien	  break;
649060484Sobrien
649160484Sobrien	case 'n':
649260484Sobrien	  if (streq (str, "no-fpu"))
649360484Sobrien	    cpu_variant &= ~FPU_ALL;
649460484Sobrien	  break;
649560484Sobrien
649660484Sobrien#ifdef OBJ_ELF
649760484Sobrien        case 'o':
649860484Sobrien          if (streq (str, "oabi"))
649960484Sobrien            target_oabi = true;
650060484Sobrien          break;
650160484Sobrien#endif
650260484Sobrien
650360484Sobrien        case 't':
650460484Sobrien          /* Limit assembler to generating only Thumb instructions: */
650560484Sobrien          if (streq (str, "thumb"))
650660484Sobrien            {
650760484Sobrien              cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB;
650860484Sobrien              cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_NONE;
650960484Sobrien              thumb_mode = 1;
651060484Sobrien            }
651160484Sobrien          else if (streq (str, "thumb-interwork"))
651260484Sobrien            {
651360484Sobrien	      if ((cpu_variant & ARM_THUMB) == 0)
651460484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V4T;
651560484Sobrien#if defined OBJ_COFF || defined OBJ_ELF
651660484Sobrien              support_interwork = true;
651760484Sobrien#endif
651860484Sobrien            }
651960484Sobrien          else
652060484Sobrien	    goto bad;
652160484Sobrien          break;
652260484Sobrien
652360484Sobrien	default:
652460484Sobrien	  if (streq (str, "all"))
652560484Sobrien	    {
652660484Sobrien	      cpu_variant = ARM_ALL | FPU_ALL;
652760484Sobrien	      return 1;
652860484Sobrien	    }
652960484Sobrien#if defined OBJ_COFF || defined OBJ_ELF
653060484Sobrien	  if (! strncmp (str, "apcs-", 5))
653160484Sobrien	    {
653260484Sobrien	      /* GCC passes on all command line options starting "-mapcs-..."
653360484Sobrien		 to us, so we must parse them here.  */
653460484Sobrien
653560484Sobrien	      str += 5;
653660484Sobrien
653760484Sobrien	      if (streq (str, "32"))
653860484Sobrien		{
653960484Sobrien		  uses_apcs_26 = false;
654060484Sobrien		  return 1;
654160484Sobrien		}
654260484Sobrien	      else if (streq (str, "26"))
654360484Sobrien		{
654460484Sobrien		  uses_apcs_26 = true;
654560484Sobrien		  return 1;
654660484Sobrien		}
654760484Sobrien	      else if (streq (str, "frame"))
654860484Sobrien		{
654960484Sobrien		  /* Stack frames are being generated - does not affect
655060484Sobrien		     linkage of code.  */
655160484Sobrien		  return 1;
655260484Sobrien		}
655360484Sobrien	      else if (streq (str, "stack-check"))
655460484Sobrien		{
655560484Sobrien		  /* Stack checking is being performed - does not affect
655660484Sobrien		     linkage, but does require that the functions
655760484Sobrien		     __rt_stkovf_split_small and __rt_stkovf_split_big be
655860484Sobrien		     present in the final link.  */
655960484Sobrien
656060484Sobrien		  return 1;
656160484Sobrien		}
656260484Sobrien	      else if (streq (str, "float"))
656360484Sobrien		{
656460484Sobrien		  /* Floating point arguments are being passed in the floating
656560484Sobrien		     point registers.  This does affect linking, since this
656660484Sobrien		     version of the APCS is incompatible with the version that
656760484Sobrien		     passes floating points in the integer registers.  */
656860484Sobrien
656960484Sobrien		  uses_apcs_float = true;
657060484Sobrien		  return 1;
657160484Sobrien		}
657260484Sobrien	      else if (streq (str, "reentrant"))
657360484Sobrien		{
657460484Sobrien		  /* Reentrant code has been generated.  This does affect
657560484Sobrien		     linking, since there is no point in linking reentrant/
657660484Sobrien		     position independent code with absolute position code. */
657760484Sobrien		  pic_code = true;
657860484Sobrien		  return 1;
657960484Sobrien		}
658060484Sobrien
658160484Sobrien	      as_bad (_("Unrecognised APCS switch -m%s"), arg);
658260484Sobrien	      return 0;
658360484Sobrien  	    }
658460484Sobrien#endif
658560484Sobrien	  /* Strip off optional "arm" */
658660484Sobrien	  if (! strncmp (str, "arm", 3))
658760484Sobrien	    str += 3;
658860484Sobrien
658960484Sobrien	  switch (*str)
659060484Sobrien	    {
659160484Sobrien	    case '1':
659260484Sobrien	      if (streq (str, "1"))
659360484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_1;
659460484Sobrien	      else
659560484Sobrien		goto bad;
659660484Sobrien	      break;
659760484Sobrien
659860484Sobrien	    case '2':
659960484Sobrien	      if (streq (str, "2"))
660060484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2;
660160484Sobrien	      else if (streq (str, "250"))
660260484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_250;
660360484Sobrien	      else
660460484Sobrien		goto bad;
660560484Sobrien	      break;
660660484Sobrien
660760484Sobrien	    case '3':
660860484Sobrien	      if (streq (str, "3"))
660960484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3;
661060484Sobrien	      else
661160484Sobrien		goto bad;
661260484Sobrien	      break;
661360484Sobrien
661460484Sobrien	    case '6':
661560484Sobrien	      switch (strtol (str, NULL, 10))
661660484Sobrien		{
661760484Sobrien		case 6:
661860484Sobrien		case 60:
661960484Sobrien		case 600:
662060484Sobrien		case 610:
662160484Sobrien		case 620:
662260484Sobrien		  cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_6;
662360484Sobrien		  break;
662460484Sobrien		default:
662560484Sobrien		  goto bad;
662660484Sobrien		}
662760484Sobrien	      break;
662860484Sobrien
662960484Sobrien	    case '7':
663060484Sobrien	      switch (strtol (str, & str, 10))	/* Eat the processor name */
663160484Sobrien		{
663260484Sobrien		case 7:
663360484Sobrien		case 70:
663460484Sobrien		case 700:
663560484Sobrien		case 710:
663660484Sobrien		case 720:
663760484Sobrien		case 7100:
663860484Sobrien		case 7500:
663960484Sobrien		  break;
664060484Sobrien		default:
664160484Sobrien		  goto bad;
664260484Sobrien		}
664360484Sobrien              cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7;
664460484Sobrien              for (; *str; str++)
664560484Sobrien                {
664660484Sobrien                switch (* str)
664760484Sobrien                  {
664860484Sobrien                  case 't':
664960484Sobrien                    cpu_variant |= (ARM_THUMB | ARM_ARCH_V4);
665060484Sobrien                    break;
665160484Sobrien
665260484Sobrien                  case 'm':
665360484Sobrien                    cpu_variant |= ARM_LONGMUL;
665460484Sobrien                    break;
665560484Sobrien
665660484Sobrien		  case 'f': /* fe => fp enabled cpu.  */
665760484Sobrien		    if (str[1] == 'e')
665860484Sobrien		      ++ str;
665960484Sobrien		    else
666060484Sobrien		      goto bad;
666160484Sobrien
666260484Sobrien		  case 'c': /* Left over from 710c processor name.  */
666360484Sobrien                  case 'd': /* Debug */
666460484Sobrien                  case 'i': /* Embedded ICE */
666560484Sobrien                    /* Included for completeness in ARM processor naming. */
666660484Sobrien                    break;
666760484Sobrien
666860484Sobrien                  default:
666960484Sobrien                    goto bad;
667060484Sobrien                  }
667160484Sobrien                }
667260484Sobrien	      break;
667360484Sobrien
667460484Sobrien	    case '8':
667560484Sobrien	      if (streq (str, "8") || streq (str, "810"))
667660484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY)
667760484Sobrien		  | ARM_8 | ARM_ARCH_V4 | ARM_LONGMUL;
667860484Sobrien	      else
667960484Sobrien		goto bad;
668060484Sobrien	      break;
668160484Sobrien
668260484Sobrien	    case '9':
668360484Sobrien	      if (streq (str, "9"))
668460484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY)
668560484Sobrien		  | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB;
668660484Sobrien	      else if (streq (str, "920"))
668760484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY)
668860484Sobrien		  | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL;
668960484Sobrien	      else if (streq (str, "920t"))
669060484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY)
669160484Sobrien		  | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB;
669260484Sobrien	      else if (streq (str, "9tdmi"))
669360484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY)
669460484Sobrien		  | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB;
669560484Sobrien	      else
669660484Sobrien		goto bad;
669760484Sobrien	      break;
669860484Sobrien
669960484Sobrien
670060484Sobrien	    case 's':
670160484Sobrien	      if (streq (str, "strongarm")
670260484Sobrien		  || streq (str, "strongarm110")
670360484Sobrien		  || streq (str, "strongarm1100"))
670460484Sobrien		cpu_variant = (cpu_variant & ~ARM_ANY)
670560484Sobrien		  | ARM_8 | ARM_ARCH_V4 | ARM_LONGMUL;
670660484Sobrien	      else
670760484Sobrien		goto bad;
670860484Sobrien	      break;
670960484Sobrien
671060484Sobrien	    case 'v':
671160484Sobrien	      /* Select variant based on architecture rather than processor.  */
671260484Sobrien	      switch (*++str)
671360484Sobrien		{
671460484Sobrien		case '2':
671560484Sobrien		  switch (*++str)
671660484Sobrien		    {
671760484Sobrien		    case 'a':
671860484Sobrien		      cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3;
671960484Sobrien		      break;
672060484Sobrien		    case 0:
672160484Sobrien		      cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2;
672260484Sobrien		      break;
672360484Sobrien		    default:
672460484Sobrien		      as_bad (_("Invalid architecture variant -m%s"), arg);
672560484Sobrien		      break;
672660484Sobrien		    }
672760484Sobrien		  break;
672860484Sobrien
672960484Sobrien		case '3':
673060484Sobrien		    cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7;
673160484Sobrien
673260484Sobrien		  switch (*++str)
673360484Sobrien		    {
673460484Sobrien		    case 'm': cpu_variant |= ARM_LONGMUL; break;
673560484Sobrien		    case 0:   break;
673660484Sobrien		    default:
673760484Sobrien		      as_bad (_("Invalid architecture variant -m%s"), arg);
673860484Sobrien		      break;
673960484Sobrien		    }
674060484Sobrien		  break;
674160484Sobrien
674260484Sobrien		case '4':
674360484Sobrien		  cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V4;
674460484Sobrien
674560484Sobrien		  switch (*++str)
674660484Sobrien		    {
674760484Sobrien		    case 't': cpu_variant |= ARM_THUMB; break;
674860484Sobrien		    case 0:   break;
674960484Sobrien		    default:
675060484Sobrien		      as_bad (_("Invalid architecture variant -m%s"), arg);
675160484Sobrien		      break;
675260484Sobrien		    }
675360484Sobrien		  break;
675460484Sobrien
675560484Sobrien		case '5':
675660484Sobrien		  cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V5;
675760484Sobrien		  switch (*++str)
675860484Sobrien		    {
675960484Sobrien		    case 't': cpu_variant |= ARM_THUMB; break;
676060484Sobrien		    case 'e': cpu_variant |= ARM_EXT_V5E; break;
676160484Sobrien		    case 0:   break;
676260484Sobrien		    default:
676360484Sobrien		      as_bad (_("Invalid architecture variant -m%s"), arg);
676460484Sobrien		      break;
676560484Sobrien		    }
676660484Sobrien		  break;
676760484Sobrien
676860484Sobrien		default:
676960484Sobrien		  as_bad (_("Invalid architecture variant -m%s"), arg);
677060484Sobrien		  break;
677160484Sobrien		}
677260484Sobrien	      break;
677360484Sobrien
677460484Sobrien	    default:
677560484Sobrien	    bad:
677660484Sobrien	      as_bad (_("Invalid processor variant -m%s"), arg);
677760484Sobrien	      return 0;
677860484Sobrien	    }
677960484Sobrien	}
678060484Sobrien      break;
678160484Sobrien
678260484Sobrien#if defined OBJ_ELF || defined OBJ_COFF
678360484Sobrien    case 'k':
678460484Sobrien      pic_code = 1;
678560484Sobrien      break;
678660484Sobrien#endif
678760484Sobrien
678860484Sobrien    default:
678960484Sobrien      return 0;
679060484Sobrien    }
679160484Sobrien
679260484Sobrien   return 1;
679360484Sobrien}
679460484Sobrien
679560484Sobrienvoid
679660484Sobrienmd_show_usage (fp)
679760484Sobrien     FILE * fp;
679860484Sobrien{
679960484Sobrien  fprintf (fp, _("\
680060484Sobrien ARM Specific Assembler Options:\n\
680160484Sobrien  -m[arm][<processor name>] select processor variant\n\
680260484Sobrien  -m[arm]v[2|2a|3|3m|4|4t|5[t][e]] select architecture variant\n\
680360484Sobrien  -mthumb                   only allow Thumb instructions\n\
680460484Sobrien  -mthumb-interwork         mark the assembled code as supporting interworking\n\
680560484Sobrien  -mall                     allow any instruction\n\
680660484Sobrien  -mfpa10, -mfpa11          select floating point architecture\n\
680760484Sobrien  -mfpe-old                 don't allow floating-point multiple instructions\n\
680860484Sobrien  -mno-fpu                  don't allow any floating-point instructions.\n\
680960484Sobrien  -k                        generate PIC code.\n"));
681060484Sobrien#if defined OBJ_COFF || defined OBJ_ELF
681160484Sobrien  fprintf (fp, _("\
681260484Sobrien  -mapcs-32, -mapcs-26      specify which ARM Procedure Calling Standard to use\n\
681360484Sobrien  -mapcs-float              floating point args are passed in FP regs\n\
681460484Sobrien  -mapcs-reentrant          the code is position independent/reentrant\n"));
681560484Sobrien  #endif
681660484Sobrien#ifdef OBJ_ELF
681760484Sobrien  fprintf (fp, _("\
681860484Sobrien  -moabi                    support the old ELF ABI\n"));
681960484Sobrien#endif
682060484Sobrien#ifdef ARM_BI_ENDIAN
682160484Sobrien  fprintf (fp, _("\
682260484Sobrien  -EB                       assemble code for a big endian cpu\n\
682360484Sobrien  -EL                       assemble code for a little endian cpu\n"));
682460484Sobrien#endif
682560484Sobrien}
682660484Sobrien
682760484Sobrien/* We need to be able to fix up arbitrary expressions in some statements.
682860484Sobrien   This is so that we can handle symbols that are an arbitrary distance from
682960484Sobrien   the pc.  The most common cases are of the form ((+/-sym -/+ . - 8) & mask),
683060484Sobrien   which returns part of an address in a form which will be valid for
683160484Sobrien   a data instruction.  We do this by pushing the expression into a symbol
683260484Sobrien   in the expr_section, and creating a fix for that.  */
683360484Sobrien
683460484Sobrienstatic void
683560484Sobrienfix_new_arm (frag, where, size, exp, pc_rel, reloc)
683660484Sobrien     fragS *       frag;
683760484Sobrien     int           where;
683860484Sobrien     short int     size;
683960484Sobrien     expressionS * exp;
684060484Sobrien     int           pc_rel;
684160484Sobrien     int           reloc;
684260484Sobrien{
684360484Sobrien  fixS *         new_fix;
684460484Sobrien  arm_fix_data * arm_data;
684560484Sobrien
684660484Sobrien  switch (exp->X_op)
684760484Sobrien    {
684860484Sobrien    case O_constant:
684960484Sobrien    case O_symbol:
685060484Sobrien    case O_add:
685160484Sobrien    case O_subtract:
685260484Sobrien      new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc);
685360484Sobrien      break;
685460484Sobrien
685560484Sobrien    default:
685660484Sobrien      new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0,
685760484Sobrien			 pc_rel, reloc);
685860484Sobrien      break;
685960484Sobrien    }
686060484Sobrien
686160484Sobrien  /* Mark whether the fix is to a THUMB instruction, or an ARM instruction */
686260484Sobrien  arm_data = (arm_fix_data *) obstack_alloc (& notes, sizeof (arm_fix_data));
686360484Sobrien  new_fix->tc_fix_data = (PTR) arm_data;
686460484Sobrien  arm_data->thumb_mode = thumb_mode;
686560484Sobrien
686660484Sobrien  return;
686760484Sobrien}
686860484Sobrien
686960484Sobrien
687060484Sobrien/* This fix_new is called by cons via TC_CONS_FIX_NEW.  */
687160484Sobrienvoid
687260484Sobriencons_fix_new_arm (frag, where, size, exp)
687360484Sobrien     fragS *       frag;
687460484Sobrien     int           where;
687560484Sobrien     int           size;
687660484Sobrien     expressionS * exp;
687760484Sobrien{
687860484Sobrien  bfd_reloc_code_real_type type;
687960484Sobrien  int pcrel = 0;
688060484Sobrien
688161843Sobrien  /* Pick a reloc.
688261843Sobrien     FIXME: @@ Should look at CPU word size.  */
688360484Sobrien  switch (size)
688460484Sobrien    {
688561843Sobrien    case 1:
688661843Sobrien      type = BFD_RELOC_8;
688761843Sobrien      break;
688860484Sobrien    case 2:
688960484Sobrien      type = BFD_RELOC_16;
689060484Sobrien      break;
689160484Sobrien    case 4:
689260484Sobrien    default:
689360484Sobrien      type = BFD_RELOC_32;
689460484Sobrien      break;
689560484Sobrien    case 8:
689660484Sobrien      type = BFD_RELOC_64;
689760484Sobrien      break;
689860484Sobrien    }
689960484Sobrien
690060484Sobrien  fix_new_exp (frag, where, (int) size, exp, pcrel, type);
690160484Sobrien}
690260484Sobrien
690360484Sobrien/* A good place to do this, although this was probably not intended
690460484Sobrien   for this kind of use.  We need to dump the literal pool before
690560484Sobrien   references are made to a null symbol pointer.  */
690660484Sobrienvoid
690760484Sobrienarm_cleanup ()
690860484Sobrien{
690960484Sobrien  if (current_poolP == NULL)
691060484Sobrien    return;
691160484Sobrien
691260484Sobrien  subseg_set (text_section, 0); /* Put it at the end of text section.  */
691360484Sobrien  s_ltorg (0);
691460484Sobrien  listing_prev_line ();
691560484Sobrien}
691660484Sobrien
691760484Sobrienvoid
691860484Sobrienarm_start_line_hook ()
691960484Sobrien{
692060484Sobrien  last_label_seen = NULL;
692160484Sobrien}
692260484Sobrien
692360484Sobrienvoid
692460484Sobrienarm_frob_label (sym)
692560484Sobrien     symbolS * sym;
692660484Sobrien{
692760484Sobrien  last_label_seen = sym;
692860484Sobrien
692960484Sobrien  ARM_SET_THUMB (sym, thumb_mode);
693060484Sobrien
693160484Sobrien#if defined OBJ_COFF || defined OBJ_ELF
693260484Sobrien  ARM_SET_INTERWORK (sym, support_interwork);
693360484Sobrien#endif
693460484Sobrien
693560484Sobrien  if (label_is_thumb_function_name)
693660484Sobrien    {
693760484Sobrien      /* When the address of a Thumb function is taken the bottom
693860484Sobrien	 bit of that address should be set.  This will allow
693960484Sobrien	 interworking between Arm and Thumb functions to work
694060484Sobrien	 correctly.  */
694160484Sobrien
694260484Sobrien      THUMB_SET_FUNC (sym, 1);
694360484Sobrien
694460484Sobrien      label_is_thumb_function_name = false;
694560484Sobrien    }
694660484Sobrien}
694760484Sobrien
694860484Sobrien/* Adjust the symbol table.  This marks Thumb symbols as distinct from
694960484Sobrien   ARM ones.  */
695060484Sobrien
695160484Sobrienvoid
695260484Sobrienarm_adjust_symtab ()
695360484Sobrien{
695460484Sobrien#ifdef OBJ_COFF
695560484Sobrien  symbolS * sym;
695660484Sobrien
695760484Sobrien  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
695860484Sobrien    {
695960484Sobrien      if (ARM_IS_THUMB (sym))
696060484Sobrien        {
696160484Sobrien	  if (THUMB_IS_FUNC (sym))
696260484Sobrien	    {
696360484Sobrien	      /* Mark the symbol as a Thumb function.  */
696460484Sobrien	      if (   S_GET_STORAGE_CLASS (sym) == C_STAT
696560484Sobrien		  || S_GET_STORAGE_CLASS (sym) == C_LABEL) /* This can happen! */
696660484Sobrien		S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC);
696760484Sobrien
696860484Sobrien	      else if (S_GET_STORAGE_CLASS (sym) == C_EXT)
696960484Sobrien		S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC);
697060484Sobrien	      else
697160484Sobrien		as_bad (_("%s: unexpected function type: %d"),
697260484Sobrien			S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym));
697360484Sobrien	    }
697460484Sobrien          else switch (S_GET_STORAGE_CLASS (sym))
697560484Sobrien            {
697660484Sobrien              case C_EXT:
697760484Sobrien                S_SET_STORAGE_CLASS (sym, C_THUMBEXT);
697860484Sobrien                break;
697960484Sobrien              case C_STAT:
698060484Sobrien                S_SET_STORAGE_CLASS (sym, C_THUMBSTAT);
698160484Sobrien                break;
698260484Sobrien              case C_LABEL:
698360484Sobrien                S_SET_STORAGE_CLASS (sym, C_THUMBLABEL);
698460484Sobrien                break;
698560484Sobrien              default: /* do nothing */
698660484Sobrien                break;
698760484Sobrien            }
698860484Sobrien        }
698960484Sobrien
699060484Sobrien      if (ARM_IS_INTERWORK (sym))
699160484Sobrien	coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_flags = 0xFF;
699260484Sobrien    }
699360484Sobrien#endif
699460484Sobrien#ifdef OBJ_ELF
699560484Sobrien  symbolS *         sym;
699660484Sobrien  char              bind;
699760484Sobrien
699860484Sobrien  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
699960484Sobrien    {
700060484Sobrien      if (ARM_IS_THUMB (sym))
700160484Sobrien        {
700260484Sobrien	  elf_symbol_type * elf_sym;
700360484Sobrien
700460484Sobrien	  elf_sym = elf_symbol (symbol_get_bfdsym (sym));
700560484Sobrien	  bind = ELF_ST_BIND (elf_sym);
700660484Sobrien
700760484Sobrien	  /* If it's a .thumb_func, declare it as so,
700860484Sobrien	     otherwise tag label as .code 16.  */
700960484Sobrien	  if (THUMB_IS_FUNC (sym))
701060484Sobrien	    elf_sym->internal_elf_sym.st_info =
701160484Sobrien	      ELF_ST_INFO (bind, STT_ARM_TFUNC);
701260484Sobrien	  else
701360484Sobrien	    elf_sym->internal_elf_sym.st_info =
701460484Sobrien	      ELF_ST_INFO (bind, STT_ARM_16BIT);
701560484Sobrien         }
701660484Sobrien     }
701760484Sobrien#endif
701860484Sobrien}
701960484Sobrien
702060484Sobrienint
702160484Sobrienarm_data_in_code ()
702260484Sobrien{
702360484Sobrien  if (thumb_mode && ! strncmp (input_line_pointer + 1, "data:", 5))
702460484Sobrien    {
702560484Sobrien      *input_line_pointer = '/';
702660484Sobrien      input_line_pointer += 5;
702760484Sobrien      *input_line_pointer = 0;
702860484Sobrien      return 1;
702960484Sobrien    }
703060484Sobrien
703160484Sobrien  return 0;
703260484Sobrien}
703360484Sobrien
703460484Sobrienchar *
703560484Sobrienarm_canonicalize_symbol_name (name)
703660484Sobrien     char * name;
703760484Sobrien{
703860484Sobrien  int len;
703960484Sobrien
704060484Sobrien  if (thumb_mode && (len = strlen (name)) > 5
704160484Sobrien      && streq (name + len - 5, "/data"))
704260484Sobrien    *(name + len - 5) = 0;
704360484Sobrien
704460484Sobrien  return name;
704560484Sobrien}
704660484Sobrien
704760484Sobrienboolean
704860484Sobrienarm_validate_fix (fixP)
704960484Sobrien     fixS * fixP;
705060484Sobrien{
705160484Sobrien  /* If the destination of the branch is a defined symbol which does not have
705260484Sobrien     the THUMB_FUNC attribute, then we must be calling a function which has
705360484Sobrien     the (interfacearm) attribute.  We look for the Thumb entry point to that
705460484Sobrien     function and change the branch to refer to that function instead.  */
705560484Sobrien  if (   fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23
705660484Sobrien      && fixP->fx_addsy != NULL
705760484Sobrien      && S_IS_DEFINED (fixP->fx_addsy)
705860484Sobrien      && ! THUMB_IS_FUNC (fixP->fx_addsy))
705960484Sobrien    {
706060484Sobrien      fixP->fx_addsy = find_real_start (fixP->fx_addsy);
706160484Sobrien      return true;
706260484Sobrien    }
706360484Sobrien
706460484Sobrien  return false;
706560484Sobrien}
706660484Sobrien
706760484Sobrien#ifdef OBJ_ELF
706860484Sobrien/* Relocations against Thumb function names must be left unadjusted,
706960484Sobrien   so that the linker can use this information to correctly set the
707060484Sobrien   bottom bit of their addresses.  The MIPS version of this function
707160484Sobrien   also prevents relocations that are mips-16 specific, but I do not
707260484Sobrien   know why it does this.
707360484Sobrien
707460484Sobrien   FIXME:
707560484Sobrien   There is one other problem that ought to be addressed here, but
707660484Sobrien   which currently is not:  Taking the address of a label (rather
707760484Sobrien   than a function) and then later jumping to that address.  Such
707860484Sobrien   addresses also ought to have their bottom bit set (assuming that
707960484Sobrien   they reside in Thumb code), but at the moment they will not.  */
708060484Sobrien
708160484Sobrienboolean
708260484Sobrienarm_fix_adjustable (fixP)
708360484Sobrien   fixS * fixP;
708460484Sobrien{
708560484Sobrien  if (fixP->fx_addsy == NULL)
708660484Sobrien    return 1;
708760484Sobrien
708860484Sobrien  /* Prevent all adjustments to global symbols. */
708960484Sobrien  if (S_IS_EXTERN (fixP->fx_addsy))
709060484Sobrien    return 0;
709160484Sobrien
709260484Sobrien  if (S_IS_WEAK (fixP->fx_addsy))
709360484Sobrien    return 0;
709460484Sobrien
709560484Sobrien  if (THUMB_IS_FUNC (fixP->fx_addsy)
709660484Sobrien      && fixP->fx_subsy == NULL)
709760484Sobrien    return 0;
709860484Sobrien
709960484Sobrien  /* We need the symbol name for the VTABLE entries */
710060484Sobrien  if (   fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
710160484Sobrien      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
710260484Sobrien    return 0;
710360484Sobrien
710460484Sobrien  return 1;
710560484Sobrien}
710660484Sobrien
710760484Sobrienconst char *
710860484Sobrienelf32_arm_target_format ()
710960484Sobrien{
711060484Sobrien  if (target_big_endian)
711160484Sobrien    if (target_oabi)
711260484Sobrien      return "elf32-bigarm-oabi";
711360484Sobrien    else
711460484Sobrien      return "elf32-bigarm";
711560484Sobrien  else
711660484Sobrien    if (target_oabi)
711760484Sobrien      return "elf32-littlearm-oabi";
711860484Sobrien    else
711960484Sobrien      return "elf32-littlearm";
712060484Sobrien}
712160484Sobrien
712260484Sobrienvoid
712360484Sobrienarmelf_frob_symbol (symp, puntp)
712460484Sobrien     symbolS * symp;
712560484Sobrien     int * puntp;
712660484Sobrien{
712760484Sobrien  elf_frob_symbol (symp, puntp);
712860484Sobrien}
712960484Sobrien
713060484Sobrienint
713160484Sobrienarm_force_relocation (fixp)
713260484Sobrien     struct fix * fixp;
713360484Sobrien{
713460484Sobrien  if (   fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
713560484Sobrien      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY
713660484Sobrien      || fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH
713760484Sobrien      || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23)
713860484Sobrien    return 1;
713960484Sobrien
714060484Sobrien  return 0;
714160484Sobrien}
714260484Sobrien
714360484Sobrienstatic bfd_reloc_code_real_type
714460484Sobrienarm_parse_reloc ()
714560484Sobrien{
714660484Sobrien  char   id[16];
714760484Sobrien  char * ip;
714860484Sobrien  unsigned int i;
714960484Sobrien  static struct
715060484Sobrien  {
715160484Sobrien    char * str;
715260484Sobrien    int    len;
715360484Sobrien    bfd_reloc_code_real_type reloc;
715460484Sobrien  }
715560484Sobrien  reloc_map[] =
715660484Sobrien  {
715760484Sobrien#define MAP(str,reloc) { str, sizeof (str)-1, reloc }
715860484Sobrien    MAP ("(got)",    BFD_RELOC_ARM_GOT32),
715960484Sobrien    MAP ("(gotoff)", BFD_RELOC_ARM_GOTOFF),
716060484Sobrien    /* ScottB: Jan 30, 1998 */
716160484Sobrien    /* Added support for parsing "var(PLT)" branch instructions */
716260484Sobrien    /* generated by GCC for PLT relocs */
716360484Sobrien    MAP ("(plt)",    BFD_RELOC_ARM_PLT32),
716460484Sobrien    { NULL, 0,         BFD_RELOC_UNUSED }
716560484Sobrien#undef MAP
716660484Sobrien  };
716760484Sobrien
716860484Sobrien  for (i = 0, ip = input_line_pointer;
716960484Sobrien       i < sizeof (id) && (isalnum (*ip) || ispunct (*ip));
717060484Sobrien       i++, ip++)
717160484Sobrien    id[i] = tolower (*ip);
717260484Sobrien
717360484Sobrien  for (i = 0; reloc_map[i].str; i++)
717460484Sobrien    if (strncmp (id, reloc_map[i].str, reloc_map[i].len) == 0)
717560484Sobrien      break;
717660484Sobrien
717760484Sobrien  input_line_pointer += reloc_map[i].len;
717860484Sobrien
717960484Sobrien  return reloc_map[i].reloc;
718060484Sobrien}
718160484Sobrien
718260484Sobrienstatic void
718360484Sobriens_arm_elf_cons (nbytes)
718460484Sobrien     int nbytes;
718560484Sobrien{
718660484Sobrien  expressionS exp;
718760484Sobrien
718860484Sobrien#ifdef md_flush_pending_output
718960484Sobrien  md_flush_pending_output ();
719060484Sobrien#endif
719160484Sobrien
719260484Sobrien  if (is_it_end_of_statement ())
719360484Sobrien    {
719460484Sobrien      demand_empty_rest_of_line ();
719560484Sobrien      return;
719660484Sobrien    }
719760484Sobrien
719860484Sobrien#ifdef md_cons_align
719960484Sobrien  md_cons_align (nbytes);
720060484Sobrien#endif
720160484Sobrien
720260484Sobrien  do
720360484Sobrien    {
720460484Sobrien      bfd_reloc_code_real_type reloc;
720560484Sobrien
720660484Sobrien      expression (& exp);
720760484Sobrien
720860484Sobrien      if (exp.X_op == O_symbol
720960484Sobrien          && * input_line_pointer == '('
721060484Sobrien          && (reloc = arm_parse_reloc()) != BFD_RELOC_UNUSED)
721160484Sobrien        {
721260484Sobrien          reloc_howto_type * howto = bfd_reloc_type_lookup (stdoutput, reloc);
721360484Sobrien          int size = bfd_get_reloc_size (howto);
721460484Sobrien
721560484Sobrien          if (size > nbytes)
721660484Sobrien            as_bad ("%s relocations do not fit in %d bytes",
721760484Sobrien		    howto->name, nbytes);
721860484Sobrien          else
721960484Sobrien            {
722060484Sobrien              register char * p = frag_more ((int) nbytes);
722160484Sobrien              int offset = nbytes - size;
722260484Sobrien
722360484Sobrien              fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
722460484Sobrien			   & exp, 0, reloc);
722560484Sobrien            }
722660484Sobrien        }
722760484Sobrien      else
722860484Sobrien        emit_expr (& exp, (unsigned int) nbytes);
722960484Sobrien    }
723060484Sobrien  while (*input_line_pointer++ == ',');
723160484Sobrien
723260484Sobrien  input_line_pointer--;		/* Put terminator back into stream.  */
723360484Sobrien  demand_empty_rest_of_line ();
723460484Sobrien}
723560484Sobrien
723660484Sobrien#endif /* OBJ_ELF */
7237